2 Read in data

european <- read_csv("01-cleaning_data_data/european_recoded.csv")
australian <- read_csv("01-cleaning_data_data/australian_recoded.csv")
dim(european)
[1] 209 115
dim(australian)
[1] 269 146
european$EU <- 1
australian$AU <- 1
all <- merge(european,australian,all = TRUE)
table(all$EU,all$AU,useNA = "always")
      
         1 <NA>
  1      0  209
  <NA> 269    0

2.1 Variables to describe dataset (can be different between contexts)

demographics_var <- c("Age","Gender","L1","speak.other.L2","study.other.L2","origins","year.studyL2","other5.other.ways","degree","roleL2.degree","study.year","prof","L2.VCE","uni1.year","Context")
l2School <- "\\.L2school$"
l2School_variables <- colnames(all)[grep(l2School,colnames(all))]
  • First language
#table(all$L1,all$Context) # too many levels - needs to be cleaned (ex tot number of languages?)
table(all$L1,useNA = "always")

         Afrikaans           Albanian            Burmese          Cantonese            Chinese 
                 1                  2                  1                  4                  7 
          Croatian              Dutch            English  English and Dutch             German 
                 1                  1                201                  2                 77 
German and English German and Turkish                  I         Indonesian            Italian 
                 2                  1                  1                  1                 89 
          Japanese           Mandarin            Persian    Persian (Farsi)           Romanian 
                 1                  5                  1                  1                  3 
           Russian             Sindhi             Slovak            Spanish            Turkish 
                 2                  1                  1                  3                  1 
         Ukrainian               <NA> 
                 1                 67 
ggplot(all,aes(x=L1,fill=Context)) + geom_bar() + coord_flip() + ggtitle("First Language") + labs(y="N. of participants",x="")+theme_bw()

#table(all$speak.other.L2,all$Context)
L2 <- data.frame(Freq=table(all$speak.other.L2)[order(table(all$speak.other.L2),decreasing = TRUE)],
                 L2=names(table(all$speak.other.L2))[order(table(all$speak.other.L2),decreasing = TRUE)]) # too many levels - needs to be cleaned (ex tot number of languages?)
head(L2)
  • origins
table(all$origins,useNA = "always")

  No  Yes <NA> 
 323   89   66 
table(all$year.studyL2)

                     0 years                   1- 3 years                    1-3 years 
                          69                           12                           11 
                   4-6 years First year of primary school                 Kindergarten 
                          61                           80                           30 
            Less than a year            more than 6 years                        Other 
                          27                           49                           72 
table(all$degree)

                    BA in Anglistik            BA in Nordamerikastudien 
                                 43                                   4 
                                HUM                             HUM.SCI 
                                129                                   6 
                                 LA      Lingue e letterature straniere 
                                 36                                  82 
Lingue, mercati e culture dell'Asia                                  QC 
                                 13                                   5 
                                SCI 
                                 86 
  • study.year in the European context is uni1.year in the Australian context
all$study.year[is.na(all$study.year)] <- all$uni1.year[is.na(all$study.year)]
#table(all$study.year)
all$study.year <- ifelse(all$study.year == "Already graduated after 5 semesters in March 2016, was interested in survery/study, sorry.","6th semester",all$study.year)
table(all$study.year)

      1st semester           1st year       2nd semester           2nd year       3rd semester 
                72                255                  5                 37                  5 
          3rd year 3rd year of Master  4th year bachelor       5th semester       6th semester 
                20                  1                  8                  2                  3 
          7th year             Master 
                 1                  3 
  • proficiency
table(all$prof,useNA = "always")

          Advanced         Elementary       Intermediate Upper-intermediate               <NA> 
                78                105                 84                146                 65 

3 Filter participants : keep only the ones that meet the inclusion criteria

all$study.year[is.na(all$study.year)] <- all$uni1.year[is.na(all$study.year)]
# Filter only subject that we want to include in the study
# names(table(all$study.year))[1] = 1st semester"
filtered <- subset(all, (study.year == "1st year") | (study.year == names(table(all$study.year))[1]) & year.studyL2 != "0 years")
table(filtered$Context)

  English in Germany     English in Italy  German in Australia Italian in Australia 
                  71                   91                   89                   75 
table(all$Context)

  English in Germany     English in Italy  German in Australia Italian in Australia 
                  96                  113                  146                  123 

4 Define demographic variables

4.1 Need to check datasets with Rcihi (why do we have QC in L1? Am I using not the latest dataset?)

all <- filtered
demographics_var <- c("Age","Gender","L1","speak.other.L2","study.other.L2","origins","year.studyL2","other5.other.ways","degree","roleL2.degree","study.year","prof","L2.VCE","uni1.year","Context")
l2School <- "\\.L2school$"
l2School_variables <- colnames(all)[grep(l2School,colnames(all))]
ggplot(all,aes(x=L1,fill=Context)) + geom_bar() + coord_flip() + ggtitle("First Language") + labs(y="N. of participants",x="") + theme_bw()

table(all$L1,all$Context)
                    
                     English in Germany English in Italy German in Australia Italian in Australia
  Afrikaans                           0                0                   1                    0
  Albanian                            0                1                   0                    0
  Cantonese                           0                0                   2                    0
  Chinese                             0                2                   2                    0
  Dutch                               1                0                   0                    0
  English                             1                0                  75                   73
  English and Dutch                   0                0                   2                    0
  German                             64                0                   0                    0
  German and English                  1                0                   1                    0
  I                                   0                0                   0                    1
  Indonesian                          0                0                   1                    0
  Italian                             0               87                   0                    0
  Japanese                            0                0                   1                    0
  Mandarin                            0                0                   1                    1
  Persian (Farsi)                     0                0                   1                    0
  Romanian                            0                0                   1                    0
  Russian                             2                0                   0                    0
  Sindhi                              0                0                   1                    0
  Spanish                             1                0                   0                    0
  Turkish                             1                0                   0                    0
  Ukrainian                           0                1                   0                    0
table(all$degree,all$L1)
                                     
                                      Afrikaans Albanian Cantonese Chinese Dutch English
  BA in Anglistik                             0        0         0       0     0       1
  BA in Nordamerikastudien                    0        0         0       0     0       0
  HUM                                         1        0         2       0     0      92
  HUM.SCI                                     0        0         0       0     0       5
  LA                                          0        0         0       0     1       0
  Lingue e letterature straniere              0        1         0       1     0       0
  Lingue, mercati e culture dell'Asia         0        0         0       1     0       0
  QC                                          0        0         0       0     0       4
  SCI                                         0        0         0       2     0      45
                                     
                                      English and Dutch German German and English  I Indonesian
  BA in Anglistik                                     0     34                  1  0          0
  BA in Nordamerikastudien                            0      4                  0  0          0
  HUM                                                 1      0                  0  1          0
  HUM.SCI                                             0      0                  0  0          0
  LA                                                  0     25                  0  0          0
  Lingue e letterature straniere                      0      0                  0  0          0
  Lingue, mercati e culture dell'Asia                 0      0                  0  0          0
  QC                                                  0      0                  0  0          0
  SCI                                                 1      0                  1  0          1
                                     
                                      Italian Japanese Mandarin Persian (Farsi) Romanian Russian
  BA in Anglistik                           0        0        0               0        0       2
  BA in Nordamerikastudien                  0        0        0               0        0       0
  HUM                                       0        0        0               0        0       0
  HUM.SCI                                   0        0        0               0        0       0
  LA                                        0        0        0               0        0       0
  Lingue e letterature straniere           75        0        0               0        0       0
  Lingue, mercati e culture dell'Asia      12        0        0               0        0       0
  QC                                        0        0        0               0        0       0
  SCI                                       0        1        2               1        1       0
                                     
                                      Sindhi Spanish Turkish Ukrainian
  BA in Anglistik                          0       1       0         0
  BA in Nordamerikastudien                 0       0       0         0
  HUM                                      0       0       0         0
  HUM.SCI                                  0       0       0         0
  LA                                       0       0       1         0
  Lingue e letterature straniere           0       0       0         1
  Lingue, mercati e culture dell'Asia      0       0       0         0
  QC                                       0       0       0         0
  SCI                                      1       0       0         0
  • Check for L1 but we decided not to filter for it
#Filter by L1
nc <- names(table(all$Context))
table(all$Context)

  English in Germany     English in Italy  German in Australia Italian in Australia 
                  71                   91                   89                   75 
l1_filter <- all[(all$Context == nc[1] & (all$L1 == "German" | all$L1 == "German and English")) | 
                    (all$Context == nc[2] & (all$L1 == "Italian")) | 
                    (all$Context == nc[3] & (all$L1 == "English" | all$L1 == "English and Dutch" | all$L1 == "German and English")) |
                    (all$Context == nc[4] & (all$L1 == "English" | all$L1 == "English and Dutch" | all$L1 == "German and English")),]
#all <- l1_filter
# do not filter for L1
all <- all
# subset demographics
demo <- subset(all,select=c("Resp.ID",demographics_var,l2School_variables))
# Numeri finali
table(l1_filter$Context)

  English in Germany     English in Italy  German in Australia Italian in Australia 
                  65                   87                   78                   73 
table(all$Context)

  English in Germany     English in Italy  German in Australia Italian in Australia 
                  71                   91                   89                   75 
  • Filter missing value:
  • Filter participants who didn’t put the degree
  • we don’t care about speak.other.L2 and study.other.L2
missing_bySample <- rowSums(is.na(demo))
names(missing_bySample) <- demo$Resp.ID
missing_byVar <- colSums(is.na(demo))
names(missing_byVar) <- colnames(demo)
barplot(missing_bySample)

d <- data.frame(miss=missing_byVar)
d$varID <- rownames(d)
ggplot(data=d,aes(x=varID,y=miss)) + geom_bar(stat="identity") + theme_bw() +theme(axis.text.x = element_text(angle = 45, hjust = 1)) 

demo_missing <- demo %>% group_by(Context) %>% summarise(roleL2.degree_na = sum(is.na(roleL2.degree)),
                                                         L2.VCE_na = sum(is.na(L2.VCE)),
                                                         other5.other.ways_na=sum(is.na(other5.other.ways )),
                                                         uni1.year_na = sum(is.na(uni1.year)),
                                                         primary1.L2school_na=sum(is.na(primary1.L2school)),
                                                         CLS3.L2school_na = sum(is.na(CLS3.L2school)),
                                                         VSL4.L2school_na=sum(is.na(VSL4.L2school)),
                                                         degree = sum(is.na(degree)),
                                                         schooL2country5.L2school_na=sum(is.na(schooL2country5.L2school)))
# We do not filter for speak.other.L2 or study.other.L2
#demo[is.na(demo$speak.other.L2),]
# teniamo
#demo[is.na(demo$study.other.L2),]
missing_bySample[names(missing_bySample) == "5166861581"]
5166861581 
        10 
#demo[is.na(demo$year.studyL2),]
missing_bySample[names(missing_bySample) == "5378798787"]
5378798787 
         3 
# remove NA from degree
#table(demo$degree,useNA = "always")
# Remove people
all <- all[!is.na(all$degree),]
table(all$Context)

  English in Germany     English in Italy  German in Australia Italian in Australia 
                  70                   91                   88                   74 

4.2 Write filtered and merged dataset

write.csv(all,file.path("02-descriptive_data/context-merged_filtered.csv"))

4.3 Descriptive plots and tables

  • Summary demographics TO DO: - to change yearL2.study.richi
# add numbers on the bar
# tabAge <- t(table(all$Age,all$Context))
# ggplot(all,aes(x=Age,fill=Context)) + geom_bar(position="dodge",colour="white")   + labs(y="N participants") + scale_y_continuous(breaks=seq(0,90,10),limits=c(0,90)) + theme_bw() + draw_grob(tableGrob(tabAge), x=2.5, y=40, width=0.3, height=0.4) + ggtitle("Participants by age")
# tabAge
tabAge <- t(table(all$Age,all$Context))
ggdf <- data.frame(Age = rep(colnames(tabAge),each=4)[!(as.numeric(tabAge) == 0)],
  N.Participants = as.numeric(tabAge)[!(as.numeric(tabAge) == 0)],
  Context = rep(rownames(tabAge),times=3)[!(as.numeric(tabAge) == 0)])
ggplot(ggdf,aes(x=Age,y=N.Participants,fill=Context)) + geom_bar(position="dodge",colour="white",stat="identity")  + scale_y_continuous(breaks=seq(0,90,10),limits=c(0,90)) + theme_bw() + ggtitle("Participants by age")+
  geom_text(aes(label = N.Participants), hjust=0.5, vjust=-0.25, size = 2.5,position=position_dodge(width=0.9)) 

# add numbers on the bar
tabAge <- t(table(all$Gender,all$Context))
ggdf <- data.frame(Gender = rep(colnames(tabAge),each=4)[!(as.numeric(tabAge) == 0)],
  N.Participants = as.numeric(tabAge)[!(as.numeric(tabAge) == 0)],
  Context = rep(rownames(tabAge),times=3)[!(as.numeric(tabAge) == 0)])
ggplot(ggdf,aes(x=Gender,y=N.Participants,fill=Context)) + geom_bar(position="dodge",colour="white",stat="identity")  + labs(y="N participants") + scale_y_continuous(breaks=seq(0,90,10),limits=c(0,90)) + theme_bw() + ggtitle("Participants by gender")+  geom_text(aes(label = N.Participants), hjust=0.5, vjust=-0.25, size = 2.5,position=position_dodge(width=0.9)) 

# add numbers on the bar
tabAge <- t(table(all$origins,all$Context))
ggplot(all,aes(x=origins,fill=Context)) + geom_bar(position="dodge",colour="white") + ggtitle("Origins by context") + scale_y_continuous(breaks=seq(0,90,10),limits=c(0,90)) + theme_bw() + draw_grob(tableGrob(tabAge), x=2, y=60, width=0.3, height=0.4) + ggtitle("Participants by origins")

tabAge
                      
                       No Yes
  English in Germany   65   5
  English in Italy     90   1
  German in Australia  63  25
  Italian in Australia 36  38
  • proficiency
tabAge <- t(table(all$prof,all$Context))
ggplot(all,aes(x=Context,fill=prof)) + geom_bar(position="dodge",colour="white") + ggtitle("Proficiency by context") + scale_y_continuous(breaks=seq(0,90,10),limits=c(0,90)) + theme_bw() + draw_grob(tableGrob(tabAge), x=2, y=80, width=0.3, height=0.4)

tabAge
                      
                       Advanced Elementary Intermediate Upper-intermediate
  English in Germany         38          0            5                 27
  English in Italy           23          2            9                 57
  German in Australia         4         32           25                 27
  Italian in Australia        0         29           29                 16
  • L2.VCE
tabAge <- t(table(all[all$Context != "English in Germany" & all$Context != "English in Italy","L2.VCE"],all[all$Context != "English in Germany" & all$Context != "English in Italy",'Context'],useNA = "always"))
tabAge <- tabAge[-3,]
ggplot(all[all$Context != "English in Germany" & all$Context != "English in Italy",],aes(x=Context,fill=L2.VCE)) + geom_bar(position="dodge",colour="white") + ggtitle("L2.VCE by context") + scale_y_continuous(breaks=seq(0,90,10),limits=c(0,90)) + theme_bw() + draw_grob(tableGrob(tabAge), x=2, y=80, width=0.3, height=0.4)

  • da mettere a posto con Richi
# year study L2
table(all$year.studyL2,all$other.year.studyL2.richi)
                              
                               BILINGUAL FIRST.YEAR.SECONDARY FOURTH.YEAR.PRIMARY LOWER.SECONDARY
  0 years                              0                    0                   0               0
  1- 3 years                           0                    0                   0               0
  1-3 years                            0                    0                   0               0
  4-6 years                            0                    0                   0               0
  First year of primary school         0                    0                   0               0
  Kindergarten                         0                    0                   0               0
  Less than a year                     0                    0                   0               0
  more than 6 years                    0                    0                   0               0
  Other                                4                   10                   5               4
                              
                               PERSONAL SECOND.YEAR.PRIMARY SECOND.YEAR.SECONDARY
  0 years                             0                   0                     0
  1- 3 years                          0                   0                     0
  1-3 years                           0                   0                     0
  4-6 years                           0                   0                     0
  First year of primary school        0                   0                     0
  Kindergarten                        0                   0                     0
  Less than a year                    0                   0                     0
  more than 6 years                   0                   0                     0
  Other                               2                   2                     2
                              
                               THIRD.YEAR.PRIMARY
  0 years                                       0
  1- 3 years                                    0
  1-3 years                                     0
  4-6 years                                     0
  First year of primary school                  0
  Kindergarten                                  0
  Less than a year                              0
  more than 6 years                             0
  Other                                        28
all$year.studyL2 <- ifelse(all$year.studyL2 == "Other",all$other.year.studyL2.richi,all$year.studyL2 )
# European context
ggplot(all[all$Context == "English in Germany" | all$Context == "English in Italy",],aes(x=degree,fill=year.studyL2)) + geom_bar(position="dodge",colour="white") + theme_bw() + ggtitle("Degree by study year L2, by Context") +  facet_grid(~Context,scales="free") + theme(axis.text.x = element_text(angle = 45, hjust = 1)) + labs(y = "N participants", x = "degree")

  • Degree of enrolment
# Australian context
tabAge <- t(table(all[all$Context == "Italian in Australia" | all$Context == "German in Australia",'degree'],all[all$Context == "Italian in Australia" | all$Context == "German in Australia",'Context']))
ggplot(all[all$Context == "Italian in Australia" | all$Context == "German in Australia",],aes(x=Context,fill=degree)) + geom_bar(position="dodge",colour="white") + theme_bw() + ggtitle("Degree in Australian Contexts") + draw_grob(tableGrob(tabAge), x=1., y=40, width=0.3, height=0.4)

tabAge
                      
                       HUM HUM.SCI QC SCI
  German in Australia   47       3  4  34
  Italian in Australia  50       2  0  22
# Australian context
tabAge <- t(table(all[all$Context == "English in Italy" | all$Context == "English in Germany",'degree'],all[all$Context == "English in Italy" | all$Context == "English in Germany",'Context']))
ggplot(all[all$Context == "English in Italy" | all$Context == "English in Germany",],aes(x=Context,fill=degree)) + geom_bar(position="dodge",colour="white") + theme_bw() + ggtitle("Degree in European Contexts")

tabAge
                    
                     BA in Anglistik BA in Nordamerikastudien LA Lingue e letterature straniere
  English in Germany              39                        4 27                              0
  English in Italy                 0                        0  0                             78
                    
                     Lingue, mercati e culture dell'Asia
  English in Germany                                   0
  English in Italy                                    13

5 Likert scales

  • scala di blues e strongly disagree in un colore completamente diverso
all_melt <- melt(all,id.vars = c("Resp.ID","Gender","Age","prof","Context","study.year"),
                        measure.vars = likert_variables_all)
all_melt$value <- factor(all_melt$value,levels=c("Strongly disagree","Disagree","Not sure","Agree","Strongly agree"))
all_melt <- all_melt %>% separate(variable,into=c("item","type"),sep="\\.",remove=FALSE)
Too few values at 646 locations: 9368, 9369, 9370, 9371, 9372, 9373, 9374, 9375, 9376, 9377, 9378, 9379, 9380, 9381, 9382, 9383, 9384, 9385, 9386, 9387, ...
ggplot(all_melt,aes(x=variable,fill=value)) + geom_bar(position = "stack",colour="black") + 
  facet_grid(Context~type,scales = "free")+theme(axis.text.x = element_text(angle = 45, hjust = 1),axis.text=element_text(size=8)) + ggtitle("Filtered dataset") + scale_fill_manual(values=c("#ca0020","#f4a582","#ffffbf","#abd9e9","#2c7bb6","grey"))

filt_sum <- all_melt %>% group_by(Context,variable,type,value) %>% dplyr::summarise(Ngroup=length(value))
ggplot(filt_sum,aes(x=value,y=Ngroup,colour=Context,group=interaction(variable, Context))) + geom_line() + geom_point() + facet_wrap(~type,scales = "free")+theme(axis.text.x = element_text(angle = 45, hjust = 1))

  • Convert Likert scales to numbers
convertToNumber <- function(column){
  column <- factor(column,levels = c("Strongly disagree","Disagree","Not sure","Agree","Strongly agree"))
  column_number <- as.numeric(column)
  return(column_number)
}
table(all$Context)

  English in Germany     English in Italy  German in Australia Italian in Australia 
                  70                   91                   88                   74 
convert_likert <- data.frame(apply(subset(all,select=likert_variables_all),2,convertToNumber))
colnames(convert_likert) <- paste0(colnames(convert_likert),"1")
likert_variables1 <- paste0(likert_variables_all,"1")
# join the converted variables to the filtered dataset
filtered_conv <- cbind(all,convert_likert)
table(filtered_conv[,likert_variables_all[4]],filtered_conv[,likert_variables1[4]],useNA = "always")
                   
                      1   2   3   4   5 <NA>
  Agree               0   0   0 121   0    0
  Disagree            0  10   0   0   0    0
  Not sure            0   0  39   0   0    0
  Strongly agree      0   0   0   0 152    0
  Strongly disagree   1   0   0   0   0    0
  <NA>                0   0   0   0   0    0
write.csv(filtered_conv,"02-descriptive_data/merged_filtered_likertNumber.csv",row.names = FALSE)

6 Correlation plot of items by context

6.1 Italian in Australia

cov <- cor(filtered_conv[filtered_conv$Context == "Italian in Australia",likert_variables1[!(likert_variables1 %in% "necessity1")]],method = "pearson",use="pairwise.complete.obs")
row_infos <- data.frame(Variables=sapply(strsplit(colnames(cov),split="\\."),function(x) x[2]))
row_infos$Variables <- as.character(row_infos$Variables)
rownames(row_infos) <- rownames(cov)
row_infos$Variables[which(is.na(row_infos$Variables))] <- c("educated")
row_infos <- row_infos[order(row_infos$Variables),,drop=FALSE]
ann_col_wide <- data.frame(Variable=unique(row_infos$Variables))
ann_colors_wide <- list(Variables=c(comm1="#bd0026",educated="#b35806", id1="#f6e8c3",instru1="#35978f",integr1="#386cb0",intr1="#ffff99",ought1="grey",post1="black",prof1="pink"))
#pheatmap(cov, main = "Italian in Australia",annotation_names_row = FALSE,cluster_cols=TRUE,cluster_rows=TRUE,annotation_col = row_infos[,1,drop=FALSE], annotation_row = row_infos[,1,drop=FALSE],  annotation_colors = ann_colors_wide,breaks=seq(-1,1,0.2),col=c("#67001f","#b2182b","#d6604d","#f4a582","#fddbc7","#f7f7f7","#d1e5f0","#92c5de","#4393c3","#2166ac","#053061"),show_colnames = FALSE,width = 7,height = 7)
###################
diag(cov) <- NA
pheatmap(cov, main = "Italian in Australia",annotation_names_row = FALSE,cluster_cols=TRUE,cluster_rows=TRUE,annotation_col = row_infos[,1,drop=FALSE], annotation_row = row_infos[,1,drop=FALSE]
,  annotation_colors = ann_colors_wide,show_colnames = FALSE,breaks = seq(-0.6,0.7,length.out = 50),width = 7,height = 7,color=colorRampPalette(brewer.pal(n = 7, name = "RdBu"))(50))

6.2 German in Australia

cov <- cor(filtered_conv[filtered_conv$Context == "German in Australia",likert_variables1[!(likert_variables1 %in% "necessity1")]],method = "pearson",use="pairwise.complete.obs")
row_infos <- data.frame(Variables=sapply(strsplit(colnames(cov),split="\\."),function(x) x[2]))
row_infos$Variables <- as.character(row_infos$Variables)
rownames(row_infos) <- rownames(cov)
row_infos$Variables[which(is.na(row_infos$Variables))] <- c("educated")
row_infos <- row_infos[order(row_infos$Variables),,drop=FALSE]
ann_col_wide <- data.frame(Variable=unique(row_infos$Variables))
ann_colors_wide <- list(Variables=c(comm1="#bd0026",educated="#b35806", id1="#f6e8c3",instru1="#35978f",integr1="#386cb0",intr1="#ffff99",ought1="grey",post1="black",prof1="pink"))
diag(cov) <- NA
pheatmap(cov, main = "German in Australia",annotation_names_row = FALSE,cluster_cols=TRUE,cluster_rows=TRUE,annotation_col = row_infos[,1,drop=FALSE], annotation_row = row_infos[,1,drop=FALSE]
,  annotation_colors = ann_colors_wide,show_colnames = FALSE,breaks = seq(-0.6,0.7,length.out = 50),width = 7,height = 7,color=colorRampPalette(brewer.pal(n = 7, name = "RdBu"))(50))

6.3 English in Germany

cov <- cor(filtered_conv[filtered_conv$Context == "English in Germany",likert_variables1[!(likert_variables1 %in% c("reconnect.comm1",    "speakersmelb.comm1","comecloser.comm1","educated1"))]],method = "pearson",use="pairwise.complete.obs")
row_infos <- data.frame(Variables=sapply(strsplit(colnames(cov),split="\\."),function(x) x[2]))
row_infos$Variables <- as.character(row_infos$Variables)
rownames(row_infos) <- rownames(cov)
row_infos$Variables[which(is.na(row_infos$Variables))] <- c("necessity")
row_infos <- row_infos[order(row_infos$Variables),,drop=FALSE]
ann_col_wide <- data.frame(Variable=unique(row_infos$Variables))
ann_colors_wide <- list(Variables=c(id1="#f6e8c3",necessity="#b35806",instru1="#35978f",integr1="#386cb0",intr1="#ffff99",ought1="grey",post1="black",prof1="pink"))
diag(cov) <- NA
pheatmap(cov, main = "English in Germany",annotation_names_row = FALSE,cluster_cols=TRUE,cluster_rows=TRUE,annotation_col = row_infos[,1,drop=FALSE], annotation_row = row_infos[,1,drop=FALSE]
,  annotation_colors = ann_colors_wide,show_colnames = FALSE,breaks = seq(-0.6,0.7,length.out = 50),width = 7,height = 7,color=colorRampPalette(brewer.pal(n = 7, name = "RdBu"))(50))

6.4 English in Italy

cov <- cor(filtered_conv[filtered_conv$Context == "English in Italy",likert_variables1[!(likert_variables1 %in% c("reconnect.comm1","speakersmelb.comm1","comecloser.comm1","educated1"))]],method = "pearson",use="pairwise.complete.obs")
row_infos <- data.frame(Variables=sapply(strsplit(colnames(cov),split="\\."),function(x) x[2]))
row_infos$Variables <- as.character(row_infos$Variables)
rownames(row_infos) <- rownames(cov)
row_infos$Variables[which(is.na(row_infos$Variables))] <- "necessity"
row_infos <- row_infos[order(row_infos$Variables),,drop=FALSE]
ann_col_wide <- data.frame(Variable=unique(row_infos$Variables))
ann_colors_wide <- list(Variables=c(comm1="#bd0026",necessity="#b35806", id1="#f6e8c3",instru1="#35978f",integr1="#386cb0",intr1="#ffff99",ought1="grey",post1="black",prof1="pink"))
diag(cov) <- NA
pheatmap(cov, main = "English in Italy",annotation_names_row = FALSE,cluster_cols=TRUE,cluster_rows=TRUE,annotation_col = row_infos[,1,drop=FALSE], annotation_row = row_infos[,1,drop=FALSE]
,  annotation_colors = ann_colors_wide,show_colnames = FALSE,breaks = seq(-0.6,0.7,length.out = 50),width = 7,height = 7,color=colorRampPalette(brewer.pal(n = 7, name = "RdBu"))(50))

6.5 All context together

cov <- cor(filtered_conv[,likert_variables1],method = "pearson",use="pairwise.complete.obs")
row_infos <- data.frame(Variables=sapply(strsplit(colnames(cov),split="\\."),function(x) x[2]))
row_infos$Variables <- as.character(row_infos$Variables)
rownames(row_infos) <- rownames(cov)
row_infos$Variables[which(is.na(row_infos$Variables))] <- c("necessity","educated")
row_infos <- row_infos[order(row_infos$Variables),,drop=FALSE]
ann_col_wide <- data.frame(Variable=unique(row_infos$Variables))
ann_colors_wide <- list(Variables=c(comm1="#bd0026",educated="orange", id1="#f6e8c3",instru1="#35978f",necessity="#b35806",integr1="#386cb0",intr1="#ffff99",ought1="grey",post1="black",prof1="pink"))
diag(cov) <- NA
pheatmap(cov, main = "All Contexts",annotation_names_row = FALSE,cluster_cols=TRUE,cluster_rows=TRUE,annotation_col = row_infos[,1,drop=FALSE], annotation_row = row_infos[,1,drop=FALSE]
,  annotation_colors = ann_colors_wide,show_colnames = FALSE,breaks = seq(-0.6,0.7,length.out = 50),width = 7,height = 7,color=colorRampPalette(brewer.pal(n = 7, name = "RdBu"))(50))

7 Evaluate internal consistency of known constructs with alpha

sets <- list(id.var=likert_variables1[grep("\\.id1$",likert_variables1)],
             ought.var=likert_variables1[grep("\\.ought1$",likert_variables1)],
             intr.var=likert_variables1[grep("\\.intr1$",likert_variables1)],
             instru.var=likert_variables1[grep("\\.instru1$",likert_variables1)],
             integr1.var=likert_variables1[grep("\\.integr1$",likert_variables1)],
             prof.var=likert_variables1[grep("\\.prof1$",likert_variables1)],
             post.var=likert_variables1[grep("\\.post1$",likert_variables1)],
             comm.var=likert_variables1[grep("\\.comm1$",likert_variables1)])
              
get_alpha <- function(dataMot,
                      var=sets$id.var){
  var_alpha <- alpha(dataMot[,var])
  dataf <- data.frame(alpha=var_alpha$total,
                    drop = var_alpha$alpha.drop)
  rownames(dataf) <- rownames(var_alpha$alpha.drop)
  return(dataf)
}
# "Italian in Australia"
ita_in_au <- do.call(rbind,lapply(sets,function(x) {
  get_alpha(data=filtered_conv[filtered_conv$Context == "Italian in Australia",],
                      var=x)}))
ita_in_au$var <- sapply(strsplit(rownames(ita_in_au),split="\\."),function(x) x[1]) 
ita_in_au$var.full <- sapply(strsplit(rownames(ita_in_au),split="\\."),function(x) x[3]) 
ita_in_au$Context <- "Italian in Australia"
rownames(ita_in_au) <- NULL
# "German in Australia"
germ_in_au <- do.call(rbind,lapply(sets,function(x) {
  get_alpha(data=filtered_conv[filtered_conv$Context == "German in Australia",],
                      var=x)}))
Some items were negatively correlated with the total scale and probably 
should be reversed.  
To do this, run the function again with the 'check.keys=TRUE' option
Some items ( knowledge.instru1 ) were negatively correlated with the total scale and 
probably should be reversed.  
To do this, run the function again with the 'check.keys=TRUE' option
germ_in_au$var <- sapply(strsplit(rownames(germ_in_au),split="\\."),function(x) x[1]) 
germ_in_au$var.full <- sapply(strsplit(rownames(germ_in_au),split="\\."),function(x) x[3]) 
germ_in_au$Context <- "German in Australia"
rownames(germ_in_au) <- NULL
# "English in Germany"
eng_in_germ <- do.call(rbind,lapply(sets[!(names(sets) %in% "comm.var")],function(x) {
  get_alpha(data=filtered_conv[filtered_conv$Context == "English in Germany",],
                      var=x)}))
Some items were negatively correlated with the total scale and probably 
should be reversed.  
To do this, run the function again with the 'check.keys=TRUE' option
Some items ( people.ought1 ) were negatively correlated with the total scale and 
probably should be reversed.  
To do this, run the function again with the 'check.keys=TRUE' option
# the ones that makes issues
get_alpha(data=filtered_conv[filtered_conv$Context == "English in Germany",],
                      var=sets$ought.var)
Some items were negatively correlated with the total scale and probably 
should be reversed.  
To do this, run the function again with the 'check.keys=TRUE' option
Some items ( people.ought1 ) were negatively correlated with the total scale and 
probably should be reversed.  
To do this, run the function again with the 'check.keys=TRUE' option
eng_in_germ$var <- sapply(strsplit(rownames(eng_in_germ),split="\\."),function(x) x[1]) 
eng_in_germ$var.full <- sapply(strsplit(rownames(eng_in_germ),split="\\."),function(x) x[3]) 
eng_in_germ$Context <- "English in Germany"
rownames(eng_in_germ) <- NULL
# "English in Italy"
eng_in_ita <- do.call(rbind,lapply(sets[!(names(sets) %in% "comm.var")],function(x) {
  get_alpha(data=filtered_conv[filtered_conv$Context == "English in Italy",],
                      var=x)}))
eng_in_ita$var <- sapply(strsplit(rownames(eng_in_ita),split="\\."),function(x) x[1]) 
eng_in_ita$var.full <- sapply(strsplit(rownames(eng_in_ita),split="\\."),function(x) x[3]) 
eng_in_ita$Context <- "English in Italy"
rownames(eng_in_ita) <- NULL
# combine
full_alpha <- rbind(eng_in_ita,eng_in_germ,germ_in_au,ita_in_au)
  • Plot alpha by variable
full_alpha %>% group_by(Context,var) %>% 
  summarise(st.alpha = unique(alpha.std.alpha),
            G6=unique(alpha.G6.smc.)) %>%
  ggplot(.,aes(x=var,y=st.alpha,colour=Context)) + geom_point() + geom_line(aes(group=Context)) + theme_bw()

all_melt <- all_melt %>% separate(variable,into=c("item","type"),sep="\\.",remove=FALSE)
Too few values at 646 locations: 9368, 9369, 9370, 9371, 9372, 9373, 9374, 9375, 9376, 9377, 9378, 9379, 9380, 9381, 9382, 9383, 9384, 9385, 9386, 9387, ...
p1=ggplot(all_melt,aes(x=variable,fill=value)) + geom_bar(position = "stack") + 
  facet_grid(Context~type,scales = "free") + ggtitle("Filtered dataset")+theme(axis.text.x = element_text(angle = 45, hjust = 1),axis.text=element_text(size=8))+theme_bw()
p2=ggplot(full_alpha,aes(x=var.full,y=drop.std.alpha,colour=Context)) + geom_point() + geom_line(aes(group=Context)) + theme_bw() + facet_wrap(~var,scales="free")
p4=ggplot(full_alpha,aes(x=var.full,y=drop.average_r,colour=Context)) + geom_point() + geom_line(aes(group=Context)) + theme_bw() + facet_wrap(~var,scales="free")
p3=full_alpha %>% group_by(Context,var) %>% 
  summarise(st.alpha = unique(alpha.std.alpha),
            G6=unique(alpha.G6.smc.)) %>%
  ggplot(.,aes(x=var,y=st.alpha,colour=Context)) + geom_point() + geom_line(aes(group=Context)) + theme(axis.text.x = element_text(angle = 45, hjust = 1),axis.text=element_text(size=8)) + theme_bw()
cowplot::plot_grid(p2,p3,nrow=2)

8 Factor Analysis with Robby’s function

8.1 Read in data

all <- read.csv(file.path("02-descriptive_data/merged_filtered_likertNumber.csv"))

8.2 Likert variables

8.3 Alpha and FA with the combined dataset

dat <- all[,likert_variables1[!(likert_variables1 %in% c("necessity1","educated1"))]]
psych::alpha(dat,use="pairwise.complete.obs")

#detach("package:ggplot2", unload=TRUE)


fa_all <- function(data4,rot,minL,maxL,nfac=0,seed=5){
  
  set.seed(seed)
  
  # Save the orignal dataset
  data_orig <- data4
  
  # Define the rotations
  orth <- c("varimax", "quartimax", "bentlerT", "equamax", "varimin", "geominT" , "bifactor" )
  obl <- c("Promax", "promax", "oblimin", "simplimax", "bentlerQ", "geominQ", "biquartimin" ,"cluster")
  
  
  
  alpha2 <- function(x){
    
    alpha.1 <- function(C, R) {
      n <- dim(C)[2]
      alpha.raw <- (1 - tr(C)/sum(C)) * (n/(n - 1))
      sumR <- sum(R)
      alpha.std <- (1 - n/sumR) * (n/(n - 1))
      smc.R <- smc(R)
      G6 <- (1 - (n - sum(smc.R))/sumR)
      av.r <- (sumR - n)/(n * (n - 1))
      mod1 <- matrix(av.r, n, n)
      Res1 <- R - mod1
      GF1 = 1 - sum(Res1^2)/sum(R^2)
      Rd <- R - diag(R)
      diag(Res1) <- 0
      GF1.off <- 1 - sum(Res1^2)/sum(Rd^2)
      sn <- n * av.r/(1 - av.r)
      Q = (2 * n^2/((n - 1)^2 * (sum(C)^3))) * (sum(C) * (tr(C %*% 
                                                               C) + (tr(C))^2) - 2 * (tr(C) * sum(C %*% C)))
      result <- list(raw = alpha.raw, std = alpha.std, G6 = G6, 
                     av.r = av.r, sn = sn, Q = Q, GF1, GF1.off)
      return(result)
    }
    
    
    if (!isCorrelation(x)) {
      item.var <- apply(x, 2, sd, na.rm = T)
      bad <- which((item.var <= 0) | is.na(item.var))
      if ((length(bad) > 0) && delete) {
        for (baddy in 1:length(bad)) {
          warning("Item = ", colnames(x)[bad][baddy], " had no variance and was deleted")
        }
        x <- x[, -bad]
        nvar <- nvar - length(bad)
      }
      response.freq <- response.frequencies(x, max = 10)
      C <- cov(x, use = "pairwise")
    }
    else {
      C <- x
    }
    
    
    
    R <- cov2cor(C)
    
    alpha.total <- alpha.1(C, R)
    
    return(alpha.total)
  }
  
  
  cicl<-0
  par1<-1
  par2<-1
  par3<-1
  while(par3>0){
    
    
    # Selezione il numero di fattori consigliati
    
    cat("Calculating the number of factors needed \n")
    
    fact <- nfac
    if(nfac==0){
      fact<-fa.parallel(data4)$nfact
    }
    
    if(fact==1){
      stop("Only one factor remains. Check the data or reduce the threshold")
    }
    
    #rot<-c("none", "varimax", "quartimax", "bentlerT", "geominT" , "bifactor", "promax", "oblimin", "simplimax", "bentlerQ", "geominQ" , "biquartimin" , "cluster" )
    
    # Matrice uota che conterra le miedie degli alpha
    
    range <- length(seq(minL,maxL,0.01))
    
    
    mean.al<-matrix(0,range,length(rot))
    
    # Sottociclo per il calcolo delle medie degli alpha (sui fattori) al variare sia della soglia sia della della rotazione
    
    cat("Calculating the loadings thresholds \n")
    
    for(b in 1:length(rot)){
      cat("- Calculating the loadings thresholds for rotation",rot[b],"\n")
      fa1<-fa(data4,fact,rotate=rot[b])
      
      a<-fa1$loadings
      class(a)<-"matrix"
      colnames(a)<-paste("F",1:fact,sep="")
      a<-as.data.frame(a)
      a<-round(a,2)
      a$D<-rownames(a)
      
      ls1<-minL
      m<-rep(0,range)
      i<-1
      nv<-fact
      
      while(ls1<=maxL+0.01){
        
        var<-lapply(1:nv,function(f)unique(a$D[abs(a[,f])>ls1]))
        names(var)<-paste(colnames(a[,1:nv]))
        
        
        # Do this if there are factors with length less than 2
        if(any(as.numeric(summary(var)[,1])<2)){
          
          # If a certain threshold and rotation gives only factors composed by 1 give it alpha=0
          if(sum(as.numeric(summary(var)[,1])>1)==0){
            m[i] <- 0
          }else{
            var1<-var[as.numeric(summary(var)[,1])>1]
            al<-sapply(1:length(var1),function(v)alpha2(data4[,var1[[v]]])$std)
            al<-data.frame(al)
            #m[i]<-mean(t(al))
            m[i]<-median(t(al))
          }
          
        }
        # Do this if ALL the factors have length greater than 1
        else{
          al<-sapply(1:nv,function(v)alpha2(data4[,var[[v]]])$std)
          al<-data.frame(al)
          #m[i]<-mean(t(al))
          m[i]<-median(t(al))
        }
        
        i<-i+1
        ls1<-ls1+0.01
        #cat(ls1)
      }
      mean.al[,b]<-m
      #cat("\n")
    }
    
    # Creazione e stampa degli andamenti delle medie degli alpha
    cat("Producing the threshold plot \n")
    
    mean.al<-as.data.frame(mean.al)
    colnames(mean.al)<-rot
    mean.al$sl<-seq(minL,maxL,0.01)
    mean.al1<-melt(mean.al,id.vars="sl")
    zp<-ggplot(mean.al1,aes(x=sl,y=value,colour=variable))+geom_line()+labs(x="Soglia Loading",y="Alpha Medio",colour="Rotazione")
    
    
    # Display the plot
    print(zp)
    
    max(mean.al)
    #print(mean.al)
    
    # Selezione della rotazione e della soglia che massimizzano le medie degli alpha
    cat("Choosing the best rotation and threshold \n")
    ind<-which(mean.al==max(mean.al),arr.ind=T)
    if(class(ind)=="matrix"){
      ind<-ind[1,]
    }
    
    rota<-names(mean.al)[ind[2]]
    sogl<-mean.al$sl[ind[1]]
    
    
    # Keep the same rotation that was selected in the first run
    rot <- rota
    
    
    # Calcolo fattoriale
    
    fa1<-fa(data4,fact,rotate=rota)
    a<-fa1$loadings
    class(a)<-"matrix"
    colnames(a)<-paste("F",1:fact,sep="")
    a<-as.data.frame(a)
    a<-round(a,2)
    a$D<-rownames(a)
    
    # Creazione dei fattori
    
    var<-lapply(1:fact,function(f)unique(a$D[abs(a[,f])>sogl]))
    names(var)<-paste(colnames(a[,1:fact]))
    
    # Display the factors
    cat("Displaying the factors \n")
    print(var)
    
    # togliamo le variabili che non entrano nei fattori al secondo ciclo
    
    nc<- ncol(data4)
    par1_names <- names(data4)[!(names(data4) %in% unlist(var))]
    data4<-data4[,names(data4) %in% unlist(var)]
    
    # Aggiorniamo il parametro di ciclo
    
    par1<-nc-ncol(data4) 
    
    # togliamo le variabili che entrano in un fattore da sole (solo se non entrano in un altro fattore)
    par4 <- 0
    par4_names <- character()
    
    len_fac <- cbind(1:fact,sapply(1:fact,function(v)length(var[[v]])))
    
    if(any(len_fac[,2]==1)){
      par4_nam<- data.frame(variable=as.character(unlist(var[ c(len_fac[len_fac[,2]==1,1])  ])))
      par4_nam$variable <- as.character(par4_nam$variable)
      par4_nam$ntimes= sapply(1:nrow(par4_nam), function(v)sum(unlist(var)%in%par4_nam[v,"variable"] ) )
      # Do this only if the single variable enetrs in a variable alone
      
      if(any(par4_nam$ntimes>1) & rot%in%obl ){
        cat("- Will NOT remove variable(s)", par4_nam$variable[par4_nam$ntimes>1],"since they contribute to multiple factors","\n")
      }
      
      par4_names <- par4_nam$variable[par4_nam$ntimes==1]
      par4 <- length(par4_names)
      data4<-data4[,!names(data4) %in% par4_names]
    }
    
    
    # Togliamo le variabili che compaiono in piu fattori (se la rotazione e obliqua non effetuiamo tale operazione)
    
    if(rota%in%obl){
      par2<-0
      s <- character()
    }else{
      s<-c(as.vector(unlist(var)))
      s<-unique(s[duplicated(s)])
      par2<-length(s)
      data4<-data4[,!names(data4) %in% s]
    }
    
    
    
    
    cicl<-cicl+1
    
    # Aggiorniamo il parametro di ciclo
    
    par3<-par1+par2+par4
    
    # Stampa diagnosi ciclo
    
    cat(paste("Unused Variable:",par1),"\n")
    cat("-",paste(par1_names),"\n")
    cat(paste("Repeated variables:",par2),"\n")
    cat("-",paste(s),"\n")
    cat(paste("Single variables:",par4),"\n")
    cat("-",paste(par4_names),"\n")
    cat(paste("Rotation used:",rota),"\n")
    cat(paste("Threshold chosen:",sogl),"\n")
    cat(paste("End interation",cicl),"\n","\n")
    
  }
  
  cat(paste("Variables excluded in the process:"),names(data_orig)[!names(data_orig)%in%names(data4)],"\n")
  return(data4)
}



#

likert_variables2 <- names(dat)

data1 <- dat[,likert_variables1[!(likert_variables1 %in% c("necessity1","educated1","reconnect.comm1", "speakersmelb.comm1", "comecloser.comm1"))]]

# Plot the correlations
corrplot(cor(data1,use = "pair"))


# check which variable does not correlated with any other vaiable
r1 <- cor(data1,use = "pair")
diag(r1) <- 0
r1 <- data.frame(r1)
temp <- data.frame(name=names(r1),cor=sapply(1:ncol(r1),function(v)any(r1[,v]>=0.3))  )
as.character(temp$name[temp$cor==F])




# Keep just the itemes with a r.cor greater or equal to 0.3
as <- psych::alpha(data1,check.keys=F)$item.stats
as$n1<-1:nrow(as)
summary(as$r.cor)
no_corr_macu <- rownames(as[abs(as$r.cor)<0.3,])
no_corr_macu
data1<-data1[,!names(data1)%in%no_corr_macu]




# check which items will decrease the alpha
al<- psych::alpha(data1)
drop<-al$alpha.drop
tot<-as.numeric(al$total$std.alpha)
drop <- drop[drop$std.alpha>tot,]
drop


# Check how much
drop$std.alpha-tot
# They don;t drop enough so leave it

#drop_alpha_macu <- rownames(drop)
#data1<-data1[,!names(data1)%in%drop_alpha_macu]




# check how many factors should be used
fap <- fa.parallel(data1)
fap


data_macu <- data1


## Perform the anlaysis for macular thickness



#Check again the out_maculiers
out_macu <- outlier(data_macu)
#abline(h=800)

#dat_imp_pheno[out_macu>800,1:20]
#out_maculiers_macu <- dat_imp_pheno$id1[out_macu>800]
#data_macu <- data_macu[out_macu<800,]




# Run the first set of analysisi and check what is cleaned out_macu!
rot=c("oblimin","promax")


# Take off the variables that give problem
#problem_var_macu <- c( "v07_spectralis" ,"v01_cyrrus" )
#data_macu <- data_macu[,!names(data_macu)%in%problem_var_macu]


library(ggplot2)
data_macu_facleaned <- fa_all(data_macu,rot,0.2,0.5)

# 0 Variable do not enter the factors
not_used_fact_macu <- names(data_macu)[!names(data_macu)%in%names(data_macu_facleaned)]
not_used_fact_macu

# Check again how many factors you need
#fa.parallel(data_macu_facleaned)

nfact_macu <- 6

# Run the actual factorial analysis on the final dataset
fa_macu<-fa(data_macu_facleaned,nfact_macu,rot="promax")

# Check whther any variable do not enter in any factor
lod <- fa_macu$loadings
class(lod) <- "matrix"
lod <- data.frame(lod)
lod$any <- apply(lod,1,function(v)any(abs(v)>=0.2) )
apply(lod[,1:nfact_macu],1,max )
lod[lod$any==F,]
# NONE, good!

# Plot the results
a<-fa_macu$loadings
class(a)<-"matrix"
colnames(a)<-paste("F",1:nfact_macu,sep="")
a<-as.data.frame(a)
a<-round(a,2)
a$D<-rownames(a)
a1 <- a
a1$D
a1 <- melt(a1,id.vars=c("D"))
a1$x <- runif(nrow(a1))
a1$inv <- ifelse(a1$value<0,"neg","pos")
a1$value[abs(a1$value)<0.2] <- 0
a1 <- a1[a1$value!=0,]

ggplot(a1)+geom_bar(aes(x=reorder(D, value) ,y=value),stat="identity")+facet_wrap(~variable,ncol = 2,scales = "free_y")+coord_flip()
#detach("package:ggplot2", unload=TRUE)


var<-lapply(1:nfact_macu,function(f)unique(a$D[abs(a[,f])>0.2]))
names(var)<-paste(colnames(a[,1:nfact_macu]))

al <- sapply(1:length(var),function(v) psych::alpha(data_macu_facleaned[,var[[v]]])$total$std.alpha)
al
# Alpha total
psych::alpha(data_macu_facleaned)$total$std.alpha
# they are ok...
psych::alpha(data_macu)$total$std.alpha


# Table of the factors
a$D <- NULL
a[abs(a)<0.2] <- 0
for(i in 1:ncol(a)){a[,i] <- as.character(a[,i])}

a[a=="0"] <- ""
loading_fact_macu <- a
loading_fact_macu

# Discriminant analysis: In this section i will test if the values of the variables kept in the dataframe by the factorial analysis are able to discriminate between subjects for which their total score lie in the 1st and 3rd quartile.
discr<-unique(data_macu_facleaned)

# Detrmine the total score

discr$punteggio<-rowSums(discr)

# Dividce the groups of people who lie in the 4rth and 1st quarile

hist(discr$punteggio)
quantile(discr$punteggio,na.rm = T)

discr1<-unique(discr[discr[,ncol(discr)]<=quantile(discr[,ncol(discr)],na.rm = T)[2],])
discr2<-unique(discr[discr[,ncol(discr)]>=quantile(discr[,ncol(discr)],na.rm = T)[4],])

# Wilcox Test the values of each single variable comparing the group of pople lien in the 1st and 3rd quartile

test<-data.frame(Item=colnames(discr[,1:(ncol(discr)-1)]),p.value=rep(0,(ncol(discr)-1)))

for(i in 1:(ncol(discr)-1)){
  test[i,2]<-wilcox.test(discr1[,i],discr2[,i],alternative="two.sided")$p.value
}

test <- test[order(test$p.value),]
test
# they all discriminate!


# Calculate factors on the discarded variables

dat_disc <- dat[,no_corr_macu]


# check how many factors should be used
fap <- fa.parallel(dat_disc)
fap



library(ggplot2)
fa_disc <- fa(dat_disc,nfactors = 1,rotate = "oblimin")



# Plot the results
a<-fa_disc$loadings
class(a)<-"matrix"
colnames(a)<-paste("F",1,sep="")
a<-as.data.frame(a)
a<-round(a,2)
a$D<-rownames(a)
a1 <- a
a1$D
a1 <- melt(a1,id.vars=c("D"))
a1$x <- runif(nrow(a1))
a1$inv <- ifelse(a1$value<0,"neg","pos")
a1$value[abs(a1$value)<0.2] <- 0
a1 <- a1[a1$value!=0,]



library(ggplot2)
ggplot(a1)+geom_bar(aes(x=reorder(D, value) ,y=value),stat="identity")+facet_wrap(~variable,ncol = 2,scales = "free_y")+coord_flip()
#detach("package:ggplot2", unload=TRUE)


# Predict the factors

pred <- as.data.frame(predict(fa_macu,dat[,names(data_macu_facleaned)]))
names(pred) <- paste("Factor",1:nfact_macu,sep = "")

# Predict the factor from the discarded variables
pred_disc <- as.data.frame(predict(fa_disc,dat[,names(dat_disc)]))
names(pred_disc) <- paste("Factor",7,sep = "")



factors <- c(names(pred),names(pred_disc))


dat_complete <- cbind(dat,scale(pred),scale(pred_disc))


corrplot(cor(dat_complete[,likert_variables2],dat_complete[,factors],use = "pair"))


all_complete <-  cbind(all,pred,pred_disc)

dat_plot <- melt(all_complete,id.vars = "Context",measure.vars = factors)

library(ggplot2)
ggplot(dat_plot)+geom_boxplot(aes(x=Context,y=value,color=Context))+facet_wrap(~variable)+coord_flip()+guides(color=F)



mod <- lm(Factor1~Context,data=all_complete)
summary(mod)

mod <- lm(Factor2~Context,data=all_complete)
summary(mod)

summary(lm(Factor4~Context,data=all_complete))

summary(lm(Factor6~Context,data=all_complete))

summary(lm(Factor7~Context,data=all_complete))

8.4 Basic factor analysis: 7 factors

Seven, is the number of factors that would be present according to the study design. Using very relaxed cutoff of 0.2 to get rid of not important variables in each factor.

usable_items <- likert_variables1[!(likert_variables1 %in% c("necessity1","educated1","reconnect.comm1", "speakersmelb.comm1", "comecloser.comm1"))]
data1 <- dat[,usable_items]

fact <- 7
fa_basic <- fa(data1,fact)

fa_basic

# plot loadings
loadings_basic <- fa_basic$loadings
class(loadings_basic)<-"matrix"
colnames(loadings_basic)<-paste("F",1:7,sep="")
loadings_basic<-as.data.frame(loadings_basic)
loadings_basic<-round(loadings_basic,2)
loadings_basic$D<-rownames(loadings_basic)
a1 <- loadings_basic

a1 <- melt(a1,id.vars=c("D"))
a1$x <- runif(nrow(a1))
a1$inv <- ifelse(a1$value<0,"neg","pos")
a1$value[abs(a1$value)<0.2] <- 0
a1 <- a1[a1$value!=0,]
a1 <- a1 %>% separate(D,into = c("Variable","Item"),remove=FALSE,sep="[.]")

ggplot(a1)+geom_bar(aes(x=reorder(D, value) ,y=value,fill=Item),stat="identity")+facet_wrap(~variable,ncol = 2,scales = "free_y")+coord_flip() + geom_hline(yintercept = c(-0.3,0.3),linetype="dotted",colour="dark red")

# Table of the factors
loadings_basic$D <- NULL
loadings_basic[abs(loadings_basic)<0.2] <- 0
for(i in 1:ncol(loadings_basic)){loadings_basic[,i] <- as.character(loadings_basic[,i])}

loadings_basic[loadings_basic=="0"] <- ""
loading_fact_reduced <- loadings_basic
loading_fact_reduced

# predict values per samples
pred_basic <- as.data.frame(predict(fa_basic,data1))
names(pred_basic) <- paste("Factor",1:fact,sep = "")

factors <- names(pred_basic)
dat_complete_basic <- cbind(dat,scale(pred_basic))
corrplot(cor(dat_complete_basic[,usable_items],dat_complete_basic[,factors],use = "pair"))

all_complete_basic <-  cbind(all,pred_basic)
dat_plot_basic <- melt(all_complete_basic,id.vars = "Context",measure.vars = factors)

library(ggplot2)
ggplot(dat_plot_basic)+geom_boxplot(aes(x=Context,y=value,color=Context))+facet_wrap(~variable)+coord_flip()+guides(color=F)

8.5 Basic factor analysis: 6 factors

Using very relaxed cutoff of 0.2 to get rid of not important variables in each factor.

usable_items <- likert_variables1[!(likert_variables1 %in% c("necessity1","educated1","reconnect.comm1", "speakersmelb.comm1", "comecloser.comm1"))]
data1 <- dat[,usable_items]

# From a statisticak point of view 
fap <- fa.parallel(data1)
fact <- 6
fa_basic <- fa(data1,fact)

fa_basic

# plot loadings
loadings_basic <- fa_basic$loadings
class(loadings_basic)<-"matrix"
colnames(loadings_basic)<-paste("F",1:6,sep="")
loadings_basic<-as.data.frame(loadings_basic)
loadings_basic<-round(loadings_basic,2)
loadings_basic$D<-rownames(loadings_basic)
a1 <- loadings_basic

a1 <- melt(a1,id.vars=c("D"))
a1$x <- runif(nrow(a1))
a1$inv <- ifelse(a1$value<0,"neg","pos")
a1$value[abs(a1$value)<0.2] <- 0
a1 <- a1[a1$value!=0,]
a1 <- a1 %>% separate(D,into = c("Variable","Item"),remove=FALSE,sep="[.]")

ggplot(a1)+geom_bar(aes(x=reorder(D, value) ,y=value,fill=Item),stat="identity")+facet_wrap(~variable,ncol = 2,scales = "free_y")+coord_flip()+ geom_hline(yintercept = c(-0.3,0.3),linetype="dotted",colour="dark red")

# Table of the factors
loadings_basic$D <- NULL
loadings_basic[abs(loadings_basic)<0.2] <- 0
for(i in 1:ncol(loadings_basic)){loadings_basic[,i] <- as.character(loadings_basic[,i])}

loadings_basic[loadings_basic == "0"] <- ""
loading_fact_reduced <- loadings_basic
loading_fact_reduced

# predict values per samples
pred_basic <- as.data.frame(predict(fa_basic,data1))
names(pred_basic) <- paste("Factor",1:fact,sep = "")

factors <- names(pred_basic)
dat_complete_basic <- cbind(dat,scale(pred_basic))
corrplot(cor(dat_complete_basic[,usable_items],dat_complete_basic[,factors],use = "pair"))

all_complete_basic <-  cbind(all,pred_basic)
dat_plot_basic <- melt(all_complete_basic,id.vars = "Context",measure.vars = factors)

library(ggplot2)
ggplot(dat_plot_basic) + geom_boxplot(aes(x=Context,y=value,color=Context))+facet_wrap(~variable)+coord_flip()+guides(color=F)

# error bar 
sum_stat <- dat_plot_basic %>% group_by(Context,variable) %>%
  summarise(meanFac = mean(value,na.rm=TRUE),
            stdFac = sd(value,na.rm=TRUE),
            nObs = length(Context[!is.na(value)])) %>%
  mutate(stdMean = stdFac/sqrt(nObs))

ggplot(sum_stat,aes(x=Context,y=meanFac,colour=Context)) + 
geom_errorbar(aes(ymin=meanFac-stdMean, ymax=meanFac+stdMean)) + facet_wrap(~variable,scales="free_y") + geom_point() +theme(axis.text.x = element_text(angle = 45, hjust = 1))

ggplot(sum_stat,aes(x=variable,y=meanFac,colour=variable)) + 
geom_errorbar(aes(ymin=meanFac-stdMean, ymax=meanFac+stdMean)) + facet_wrap(~Context,scales="free_y") + geom_point()

kable(sum_stat)

9 Factor analysis on each context separately

usable_items <- likert_variables1[!(likert_variables1 %in% c("necessity1","educated1","reconnect.comm1", "speakersmelb.comm1", "comecloser.comm1"))]
# 
dat <- all[,c(usable_items,"Context")]
dat_noNA <- dat[rowSums(is.na(dat)) == 0,]
all_noNA <- all[rowSums(is.na(dat)) == 0,]

# na to remove
table(rowSums(is.na(dat)),dat$Context)

get_residuals <- function(item,pred = dat$Context){
  mod <- lm(item ~ pred)
  return(mod$residuals)
}

dat_onlyItems <- dat_noNA[,usable_items]
#data1 <- dat[,usable_items]
# dat_onlyItems <- data1

applygetRes <- apply(as.matrix(dat_onlyItems),2,get_residuals,pred=dat_noNA$Context)

# Factanal 
# From a statisticak point of view 
fap <- fa.parallel(applygetRes)
fact <- 6
fa_basic <- fa(applygetRes,fact)

fa_basic

# plot loadings
loadings_basic <- fa_basic$loadings
class(loadings_basic)<-"matrix"
colnames(loadings_basic)<-paste("F",1:6,sep="")
loadings_basic<-as.data.frame(loadings_basic)
loadings_basic<-round(loadings_basic,2)
loadings_basic$D<-rownames(loadings_basic)
a1 <- loadings_basic

a1 <- melt(a1,id.vars=c("D"))
a1$x <- runif(nrow(a1))
a1$inv <- ifelse(a1$value<0,"neg","pos")
a1$value[abs(a1$value)<0.2] <- 0
a1 <- a1[a1$value!=0,]
a1 <- a1 %>% separate(D,into = c("Variable","Item"),remove=FALSE,sep="[.]")

ggplot(a1)+geom_bar(aes(x=reorder(D, value) ,y=value,fill=Item),stat="identity")+facet_wrap(~variable,ncol = 2,scales = "free_y")+coord_flip()+ geom_hline(yintercept = c(-0.3,0.3),linetype="dotted",colour="dark red")

# Table of the factors
loadings_basic$D <- NULL
loadings_basic[abs(loadings_basic)<0.2] <- 0
for(i in 1:ncol(loadings_basic)){loadings_basic[,i] <- as.character(loadings_basic[,i])}

loadings_basic[loadings_basic == "0"] <- ""
loading_fact_reduced <- loadings_basic
loading_fact_reduced

# predict values per samples
pred_basic <- as.data.frame(predict(fa_basic,dat_onlyItems))
names(pred_basic) <- paste("Factor",1:fact,sep = "")

factors <- names(pred_basic)
dat_complete_basic <- cbind(dat_onlyItems,scale(pred_basic))
corrplot(cor(dat_complete_basic[,usable_items],dat_complete_basic[,factors],use = "pair"))

all_complete_basic <-  cbind(all_noNA,pred_basic)
dat_plot_basic <- melt(all_complete_basic,id.vars = "Context",measure.vars = factors)

library(ggplot2)
ggplot(dat_plot_basic) + geom_boxplot(aes(x=Context,y=value,color=Context))+facet_wrap(~variable)+coord_flip()+guides(color=F)

# error bar 
sum_stat <- dat_plot_basic %>% group_by(Context,variable) %>%
  summarise(meanFac = mean(value,na.rm=TRUE),
            stdFac = sd(value,na.rm=TRUE),
            nObs = length(Context[!is.na(value)])) %>%
  mutate(stdMean = stdFac/sqrt(nObs))

ggplot(sum_stat,aes(x=Context,y=meanFac,colour=Context)) + 
geom_errorbar(aes(ymin=meanFac-stdMean, ymax=meanFac+stdMean)) + facet_wrap(~variable,scales="free_y") + geom_point() +theme(axis.text.x = element_text(angle = 45, hjust = 1))

ggplot(sum_stat,aes(x=variable,y=meanFac,colour=variable)) + 
geom_errorbar(aes(ymin=meanFac-stdMean, ymax=meanFac+stdMean)) + facet_wrap(~Context,scales="free_y") + geom_point()

kable(sum_stat)

## linear model

9.1 Demographics

demographics_var <- c("Age","Gender","L1","speak.other.L2","study.other.L2","origins","year.studyL2","other5.other.ways","degree","roleL2.degree","study.year","prof","L2.VCE","uni1.year","Context")

9.2 Only English in Germany

usable_items <- likert_variables1[!(likert_variables1 %in% c("necessity1","educated1","reconnect.comm1", "speakersmelb.comm1", "comecloser.comm1"))]
data1 <- all[all$Context %in% "English in Germany",usable_items]

# From a statisticak point of view 
fap <- fa.parallel(data1)
fact <- 6
fa_basic <- fa(data1,fact)

fa_basic

# plot loadings
loadings_basic <- fa_basic$loadings
class(loadings_basic)<-"matrix"
colnames(loadings_basic)<-paste("F",1:6,sep="")
loadings_basic<-as.data.frame(loadings_basic)
loadings_basic<-round(loadings_basic,2)
loadings_basic$D<-rownames(loadings_basic)
a1 <- loadings_basic

a1 <- melt(a1,id.vars=c("D"))
a1$x <- runif(nrow(a1))
a1$inv <- ifelse(a1$value<0,"neg","pos")
a1$value[abs(a1$value)<0.2] <- 0
a1 <- a1[a1$value!=0,]
a1 <- a1 %>% separate(D,into = c("Variable","Item"),remove=FALSE,sep="[.]")

ggplot(a1)+geom_bar(aes(x=reorder(D, value) ,y=value,fill=Item),stat="identity")+facet_wrap(~variable,ncol = 2,scales = "free_y")+coord_flip()+ geom_hline(yintercept = c(-0.3,0.3),linetype="dotted",colour="dark red")

# Table of the factors
loadings_basic$D <- NULL
loadings_basic[abs(loadings_basic)<0.2] <- 0
for(i in 1:ncol(loadings_basic)){loadings_basic[,i] <- as.character(loadings_basic[,i])}

loadings_basic[loadings_basic=="0"] <- ""
loading_fact_reduced <- loadings_basic
loading_fact_reduced

# predict values per samples
pred_basic <- as.data.frame(predict(fa_basic,data1))
names(pred_basic) <- paste("Factor",1:fact,sep = "")

factors <- names(pred_basic)
dat_complete_basic <- cbind(dat,scale(pred_basic))
corrplot(cor(dat_complete_basic[,usable_items],dat_complete_basic[,factors],use = "pair"))

all_complete_basic <-  cbind(all,pred_basic)
dat_plot_basic <- melt(all_complete_basic,id.vars = "Context",measure.vars = factors)

library(ggplot2)
ggplot(dat_plot_basic)+geom_boxplot(aes(x=Context,y=value,color=Context))+facet_wrap(~variable)+coord_flip()+guides(color=F)
LS0tCnRpdGxlOiAiQW5hbHlzaXMgb2YgbWVyZ2VkIHF1ZXN0aW9ubmFpcmVzIgphdXRob3I6ICJBbm5hIFF1YWdsaWVyaSAmIFJpY2NhcmRvIEFtb3JhdGkiCmRhdGU6ICIwMy8wOS8yMDE3IgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6ICc0JwogIGdpdGh1Yl9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICBmaWdfY2FwdGlvbjogeWVzCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCi0tLQoKIyBQbGFuIHRoYXQgSSB3cm90ZSB3aXRoIFJpY2hpJ3MgY29tbWVudHMKCkxpbmsgYXQgaHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vZG9jdW1lbnQvZC8xYmROZU9NQVlZOTBrOEZBYlBSV0dCZ1MxWUJFZFAwOWtQMHZjVzB0TmdQYy9lZGl0P3VzcD1zaGFyaW5nCgoKYGBge3IsbWVzc2FnZT1GQUxTRSwgZWNobz1GQUxTRX0KbGlicmFyeShyZWFkcikKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkoY29ycnBsb3QpCmxpYnJhcnkocHN5Y2gpCmxpYnJhcnkocGhlYXRtYXApCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkocGhlYXRtYXApCmxpYnJhcnkoc2pQbG90KQpsaWJyYXJ5KHNqbGFiZWxsZWQpCmxpYnJhcnkoc2ptaXNjKQpsaWJyYXJ5KGtuaXRyKQoKZGF0YShlZmMpCnRoZW1lX3NldCh0aGVtZV9zanBsb3QoKSkKCiMgQ2h1bmsgb3B0aW9ucwprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIHByb21wdCA9IFRSVUUsY2FjaGUgPSBUUlVFLGZpZy53aWR0aCA9IDEyLGZpZy5oZWlnaHQgPSAxMikKCmBgYAoKIyBSZWFkIGluIGRhdGEKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsbWVzc2FnZT1GQUxTRX0KZXVyb3BlYW4gPC0gcmVhZF9jc3YoIjAxLWNsZWFuaW5nX2RhdGFfZGF0YS9ldXJvcGVhbl9yZWNvZGVkLmNzdiIpCmF1c3RyYWxpYW4gPC0gcmVhZF9jc3YoIjAxLWNsZWFuaW5nX2RhdGFfZGF0YS9hdXN0cmFsaWFuX3JlY29kZWQuY3N2IikKCmRpbShldXJvcGVhbikKZGltKGF1c3RyYWxpYW4pCgpldXJvcGVhbiRFVSA8LSAxCmF1c3RyYWxpYW4kQVUgPC0gMQoKYWxsIDwtIG1lcmdlKGV1cm9wZWFuLGF1c3RyYWxpYW4sYWxsID0gVFJVRSkKCnRhYmxlKGFsbCRFVSxhbGwkQVUsdXNlTkEgPSAiYWx3YXlzIikKYGBgCgojIyBWYXJpYWJsZXMgdG8gZGVzY3JpYmUgZGF0YXNldCAoY2FuIGJlIGRpZmZlcmVudCBiZXR3ZWVuIGNvbnRleHRzKQoKYGBge3J9CmRlbW9ncmFwaGljc192YXIgPC0gYygiQWdlIiwiR2VuZGVyIiwiTDEiLCJzcGVhay5vdGhlci5MMiIsInN0dWR5Lm90aGVyLkwyIiwib3JpZ2lucyIsInllYXIuc3R1ZHlMMiIsIm90aGVyNS5vdGhlci53YXlzIiwiZGVncmVlIiwicm9sZUwyLmRlZ3JlZSIsInN0dWR5LnllYXIiLCJwcm9mIiwiTDIuVkNFIiwidW5pMS55ZWFyIiwiQ29udGV4dCIpCmwyU2Nob29sIDwtICJcXC5MMnNjaG9vbCQiCmwyU2Nob29sX3ZhcmlhYmxlcyA8LSBjb2xuYW1lcyhhbGwpW2dyZXAobDJTY2hvb2wsY29sbmFtZXMoYWxsKSldCmBgYAoKLSBGaXJzdCBsYW5ndWFnZQoKYGBge3J9CiN0YWJsZShhbGwkTDEsYWxsJENvbnRleHQpICMgdG9vIG1hbnkgbGV2ZWxzIC0gbmVlZHMgdG8gYmUgY2xlYW5lZCAoZXggdG90IG51bWJlciBvZiBsYW5ndWFnZXM/KQp0YWJsZShhbGwkTDEsdXNlTkEgPSAiYWx3YXlzIikKZ2dwbG90KGFsbCxhZXMoeD1MMSxmaWxsPUNvbnRleHQpKSArIGdlb21fYmFyKCkgKyBjb29yZF9mbGlwKCkgKyBnZ3RpdGxlKCJGaXJzdCBMYW5ndWFnZSIpICsgbGFicyh5PSJOLiBvZiBwYXJ0aWNpcGFudHMiLHg9IiIpK3RoZW1lX2J3KCkKI3RhYmxlKGFsbCRzcGVhay5vdGhlci5MMixhbGwkQ29udGV4dCkKYGBgCgpgYGB7cn0KTDIgPC0gZGF0YS5mcmFtZShGcmVxPXRhYmxlKGFsbCRzcGVhay5vdGhlci5MMilbb3JkZXIodGFibGUoYWxsJHNwZWFrLm90aGVyLkwyKSxkZWNyZWFzaW5nID0gVFJVRSldLAogICAgICAgICAgICAgICAgIEwyPW5hbWVzKHRhYmxlKGFsbCRzcGVhay5vdGhlci5MMikpW29yZGVyKHRhYmxlKGFsbCRzcGVhay5vdGhlci5MMiksZGVjcmVhc2luZyA9IFRSVUUpXSkgIyB0b28gbWFueSBsZXZlbHMgLSBuZWVkcyB0byBiZSBjbGVhbmVkIChleCB0b3QgbnVtYmVyIG9mIGxhbmd1YWdlcz8pCmhlYWQoTDIpCmBgYAoKLSBvcmlnaW5zCgpgYGB7cn0KdGFibGUoYWxsJG9yaWdpbnMsdXNlTkEgPSAiYWx3YXlzIikKYGBgCgoKYGBge3J9CnRhYmxlKGFsbCR5ZWFyLnN0dWR5TDIpCmBgYAoKYGBge3J9CnRhYmxlKGFsbCRkZWdyZWUpCmBgYAoKLSBzdHVkeS55ZWFyIGluIHRoZSBFdXJvcGVhbiBjb250ZXh0IGlzIHVuaTEueWVhciBpbiB0aGUgQXVzdHJhbGlhbiBjb250ZXh0CgpgYGB7cn0KYWxsJHN0dWR5LnllYXJbaXMubmEoYWxsJHN0dWR5LnllYXIpXSA8LSBhbGwkdW5pMS55ZWFyW2lzLm5hKGFsbCRzdHVkeS55ZWFyKV0KI3RhYmxlKGFsbCRzdHVkeS55ZWFyKQphbGwkc3R1ZHkueWVhciA8LSBpZmVsc2UoYWxsJHN0dWR5LnllYXIgPT0gIkFscmVhZHkgZ3JhZHVhdGVkIGFmdGVyIDUgc2VtZXN0ZXJzIGluIE1hcmNoIDIwMTYsIHdhcyBpbnRlcmVzdGVkIGluIHN1cnZlcnkvc3R1ZHksIHNvcnJ5LiIsIjZ0aCBzZW1lc3RlciIsYWxsJHN0dWR5LnllYXIpCnRhYmxlKGFsbCRzdHVkeS55ZWFyKQpgYGAKCi0gcHJvZmljaWVuY3kKCmBgYHtyfQp0YWJsZShhbGwkcHJvZix1c2VOQSA9ICJhbHdheXMiKQpgYGAKCiMgRmlsdGVyIHBhcnRpY2lwYW50cyA6IGtlZXAgb25seSB0aGUgb25lcyB0aGF0IG1lZXQgdGhlIGluY2x1c2lvbiBjcml0ZXJpYQoKYGBge3IgRmlsdGVyZWQsZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MTV9CmFsbCRzdHVkeS55ZWFyW2lzLm5hKGFsbCRzdHVkeS55ZWFyKV0gPC0gYWxsJHVuaTEueWVhcltpcy5uYShhbGwkc3R1ZHkueWVhcildCiMgRmlsdGVyIG9ubHkgc3ViamVjdCB0aGF0IHdlIHdhbnQgdG8gaW5jbHVkZSBpbiB0aGUgc3R1ZHkKIyBuYW1lcyh0YWJsZShhbGwkc3R1ZHkueWVhcikpWzFdID0gMXN0IHNlbWVzdGVyIgoKZmlsdGVyZWQgPC0gc3Vic2V0KGFsbCwgKHN0dWR5LnllYXIgPT0gIjFzdCB5ZWFyIikgfCAoc3R1ZHkueWVhciA9PSBuYW1lcyh0YWJsZShhbGwkc3R1ZHkueWVhcikpWzFdKSAmIHllYXIuc3R1ZHlMMiAhPSAiMCB5ZWFycyIpCgp0YWJsZShmaWx0ZXJlZCRDb250ZXh0KQp0YWJsZShhbGwkQ29udGV4dCkKYGBgCgojIERlZmluZSBkZW1vZ3JhcGhpYyB2YXJpYWJsZXMKCiMjIE5lZWQgdG8gY2hlY2sgZGF0YXNldHMgd2l0aCBSY2loaSAod2h5IGRvIHdlIGhhdmUgUUMgaW4gTDE/IEFtIEkgdXNpbmcgbm90IHRoZSBsYXRlc3QgZGF0YXNldD8pCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQphbGwgPC0gZmlsdGVyZWQKZGVtb2dyYXBoaWNzX3ZhciA8LSBjKCJBZ2UiLCJHZW5kZXIiLCJMMSIsInNwZWFrLm90aGVyLkwyIiwic3R1ZHkub3RoZXIuTDIiLCJvcmlnaW5zIiwieWVhci5zdHVkeUwyIiwib3RoZXI1Lm90aGVyLndheXMiLCJkZWdyZWUiLCJyb2xlTDIuZGVncmVlIiwic3R1ZHkueWVhciIsInByb2YiLCJMMi5WQ0UiLCJ1bmkxLnllYXIiLCJDb250ZXh0IikKbDJTY2hvb2wgPC0gIlxcLkwyc2Nob29sJCIKbDJTY2hvb2xfdmFyaWFibGVzIDwtIGNvbG5hbWVzKGFsbClbZ3JlcChsMlNjaG9vbCxjb2xuYW1lcyhhbGwpKV0KCmdncGxvdChhbGwsYWVzKHg9TDEsZmlsbD1Db250ZXh0KSkgKyBnZW9tX2JhcigpICsgY29vcmRfZmxpcCgpICsgZ2d0aXRsZSgiRmlyc3QgTGFuZ3VhZ2UiKSArIGxhYnMoeT0iTi4gb2YgcGFydGljaXBhbnRzIix4PSIiKSArIHRoZW1lX2J3KCkKCnRhYmxlKGFsbCRMMSxhbGwkQ29udGV4dCkKCnRhYmxlKGFsbCRkZWdyZWUsYWxsJEwxKQpgYGAKCi0gQ2hlY2sgZm9yIEwxIGJ1dCB3ZSBkZWNpZGVkIG5vdCB0byBmaWx0ZXIgZm9yIGl0CgpgYGB7cn0KI0ZpbHRlciBieSBMMQoKbmMgPC0gbmFtZXModGFibGUoYWxsJENvbnRleHQpKQp0YWJsZShhbGwkQ29udGV4dCkKbDFfZmlsdGVyIDwtIGFsbFsoYWxsJENvbnRleHQgPT0gbmNbMV0gJiAoYWxsJEwxID09ICJHZXJtYW4iIHwgYWxsJEwxID09ICJHZXJtYW4gYW5kIEVuZ2xpc2giKSkgfCAKICAgICAgICAgICAgICAgICAgICAoYWxsJENvbnRleHQgPT0gbmNbMl0gJiAoYWxsJEwxID09ICJJdGFsaWFuIikpIHwgCiAgICAgICAgICAgICAgICAgICAgKGFsbCRDb250ZXh0ID09IG5jWzNdICYgKGFsbCRMMSA9PSAiRW5nbGlzaCIgfCBhbGwkTDEgPT0gIkVuZ2xpc2ggYW5kIER1dGNoIiB8IGFsbCRMMSA9PSAiR2VybWFuIGFuZCBFbmdsaXNoIikpIHwKICAgICAgICAgICAgICAgICAgICAoYWxsJENvbnRleHQgPT0gbmNbNF0gJiAoYWxsJEwxID09ICJFbmdsaXNoIiB8IGFsbCRMMSA9PSAiRW5nbGlzaCBhbmQgRHV0Y2giIHwgYWxsJEwxID09ICJHZXJtYW4gYW5kIEVuZ2xpc2giKSksXQoKCiNhbGwgPC0gbDFfZmlsdGVyCgojIGRvIG5vdCBmaWx0ZXIgZm9yIEwxCmFsbCA8LSBhbGwKCiMgc3Vic2V0IGRlbW9ncmFwaGljcwpkZW1vIDwtIHN1YnNldChhbGwsc2VsZWN0PWMoIlJlc3AuSUQiLGRlbW9ncmFwaGljc192YXIsbDJTY2hvb2xfdmFyaWFibGVzKSkKCiMgTnVtZXJpIGZpbmFsaQp0YWJsZShsMV9maWx0ZXIkQ29udGV4dCkKdGFibGUoYWxsJENvbnRleHQpCgpgYGAKCi0gRmlsdGVyIG1pc3NpbmcgdmFsdWU6CiAgLSBGaWx0ZXIgcGFydGljaXBhbnRzIHdobyBkaWRuJ3QgcHV0IHRoZSBkZWdyZWUKICAtIHdlIGRvbid0IGNhcmUgYWJvdXQgc3BlYWsub3RoZXIuTDIgYW5kIHN0dWR5Lm90aGVyLkwyCgpgYGB7cn0KbWlzc2luZ19ieVNhbXBsZSA8LSByb3dTdW1zKGlzLm5hKGRlbW8pKQpuYW1lcyhtaXNzaW5nX2J5U2FtcGxlKSA8LSBkZW1vJFJlc3AuSUQKbWlzc2luZ19ieVZhciA8LSBjb2xTdW1zKGlzLm5hKGRlbW8pKQpuYW1lcyhtaXNzaW5nX2J5VmFyKSA8LSBjb2xuYW1lcyhkZW1vKQoKYmFycGxvdChtaXNzaW5nX2J5U2FtcGxlKQpkIDwtIGRhdGEuZnJhbWUobWlzcz1taXNzaW5nX2J5VmFyKQpkJHZhcklEIDwtIHJvd25hbWVzKGQpCmdncGxvdChkYXRhPWQsYWVzKHg9dmFySUQseT1taXNzKSkgKyBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpICsgdGhlbWVfYncoKSArdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgCgoKZGVtb19taXNzaW5nIDwtIGRlbW8gJT4lIGdyb3VwX2J5KENvbnRleHQpICU+JSBzdW1tYXJpc2Uocm9sZUwyLmRlZ3JlZV9uYSA9IHN1bShpcy5uYShyb2xlTDIuZGVncmVlKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEwyLlZDRV9uYSA9IHN1bShpcy5uYShMMi5WQ0UpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3RoZXI1Lm90aGVyLndheXNfbmE9c3VtKGlzLm5hKG90aGVyNS5vdGhlci53YXlzICkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bmkxLnllYXJfbmEgPSBzdW0oaXMubmEodW5pMS55ZWFyKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByaW1hcnkxLkwyc2Nob29sX25hPXN1bShpcy5uYShwcmltYXJ5MS5MMnNjaG9vbCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDTFMzLkwyc2Nob29sX25hID0gc3VtKGlzLm5hKENMUzMuTDJzY2hvb2wpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVlNMNC5MMnNjaG9vbF9uYT1zdW0oaXMubmEoVlNMNC5MMnNjaG9vbCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWdyZWUgPSBzdW0oaXMubmEoZGVncmVlKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjaG9vTDJjb3VudHJ5NS5MMnNjaG9vbF9uYT1zdW0oaXMubmEoc2Nob29MMmNvdW50cnk1Lkwyc2Nob29sKSkpCgojIFdlIGRvIG5vdCBmaWx0ZXIgZm9yIHNwZWFrLm90aGVyLkwyIG9yIHN0dWR5Lm90aGVyLkwyCgojZGVtb1tpcy5uYShkZW1vJHNwZWFrLm90aGVyLkwyKSxdCiMgdGVuaWFtbwojZGVtb1tpcy5uYShkZW1vJHN0dWR5Lm90aGVyLkwyKSxdCm1pc3NpbmdfYnlTYW1wbGVbbmFtZXMobWlzc2luZ19ieVNhbXBsZSkgPT0gIjUxNjY4NjE1ODEiXQojZGVtb1tpcy5uYShkZW1vJHllYXIuc3R1ZHlMMiksXQptaXNzaW5nX2J5U2FtcGxlW25hbWVzKG1pc3NpbmdfYnlTYW1wbGUpID09ICI1Mzc4Nzk4Nzg3Il0KCiMgcmVtb3ZlIE5BIGZyb20gZGVncmVlCiN0YWJsZShkZW1vJGRlZ3JlZSx1c2VOQSA9ICJhbHdheXMiKQojIFJlbW92ZSBwZW9wbGUKYWxsIDwtIGFsbFshaXMubmEoYWxsJGRlZ3JlZSksXQoKCnRhYmxlKGFsbCRDb250ZXh0KQpgYGAKCiMjIFdyaXRlIGZpbHRlcmVkIGFuZCBtZXJnZWQgZGF0YXNldAoKYGBge3J9CndyaXRlLmNzdihhbGwsZmlsZS5wYXRoKCIwMi1kZXNjcmlwdGl2ZV9kYXRhL2NvbnRleHQtbWVyZ2VkX2ZpbHRlcmVkLmNzdiIpKQpgYGAKCiMjIERlc2NyaXB0aXZlIHBsb3RzIGFuZCB0YWJsZXMKCi0gU3VtbWFyeSBkZW1vZ3JhcGhpY3MgVE8gRE86IC0gdG8gY2hhbmdlIHllYXJMMi5zdHVkeS5yaWNoaSAKCmBgYHtyIGFnZV9ieV9jb250ZXh0LG1lc3NhZ2U9RkFMU0V9CiMgYWRkIG51bWJlcnMgb24gdGhlIGJhcgoKIyB0YWJBZ2UgPC0gdCh0YWJsZShhbGwkQWdlLGFsbCRDb250ZXh0KSkKIyBnZ3Bsb3QoYWxsLGFlcyh4PUFnZSxmaWxsPUNvbnRleHQpKSArIGdlb21fYmFyKHBvc2l0aW9uPSJkb2RnZSIsY29sb3VyPSJ3aGl0ZSIpICAgKyBsYWJzKHk9Ik4gcGFydGljaXBhbnRzIikgKyBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLDkwLDEwKSxsaW1pdHM9YygwLDkwKSkgKyB0aGVtZV9idygpICsgZHJhd19ncm9iKHRhYmxlR3JvYih0YWJBZ2UpLCB4PTIuNSwgeT00MCwgd2lkdGg9MC4zLCBoZWlnaHQ9MC40KSArIGdndGl0bGUoIlBhcnRpY2lwYW50cyBieSBhZ2UiKQojIHRhYkFnZQoKdGFiQWdlIDwtIHQodGFibGUoYWxsJEFnZSxhbGwkQ29udGV4dCkpCmdnZGYgPC0gZGF0YS5mcmFtZShBZ2UgPSByZXAoY29sbmFtZXModGFiQWdlKSxlYWNoPTQpWyEoYXMubnVtZXJpYyh0YWJBZ2UpID09IDApXSwKICBOLlBhcnRpY2lwYW50cyA9IGFzLm51bWVyaWModGFiQWdlKVshKGFzLm51bWVyaWModGFiQWdlKSA9PSAwKV0sCiAgQ29udGV4dCA9IHJlcChyb3duYW1lcyh0YWJBZ2UpLHRpbWVzPTMpWyEoYXMubnVtZXJpYyh0YWJBZ2UpID09IDApXSkKCmdncGxvdChnZ2RmLGFlcyh4PUFnZSx5PU4uUGFydGljaXBhbnRzLGZpbGw9Q29udGV4dCkpICsgZ2VvbV9iYXIocG9zaXRpb249ImRvZGdlIixjb2xvdXI9IndoaXRlIixzdGF0PSJpZGVudGl0eSIpICArIHNjYWxlX3lfY29udGludW91cyhicmVha3M9c2VxKDAsOTAsMTApLGxpbWl0cz1jKDAsOTApKSArIHRoZW1lX2J3KCkgKyBnZ3RpdGxlKCJQYXJ0aWNpcGFudHMgYnkgYWdlIikrCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IE4uUGFydGljaXBhbnRzKSwgaGp1c3Q9MC41LCB2anVzdD0tMC4yNSwgc2l6ZSA9IDIuNSxwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSh3aWR0aD0wLjkpKSAKCmBgYAoKYGBge3IgZ2VuZGVyX2J5X2NvbnRleHQsbWVzc2FnZT1GQUxTRX0KIyBhZGQgbnVtYmVycyBvbiB0aGUgYmFyCgp0YWJBZ2UgPC0gdCh0YWJsZShhbGwkR2VuZGVyLGFsbCRDb250ZXh0KSkKZ2dkZiA8LSBkYXRhLmZyYW1lKEdlbmRlciA9IHJlcChjb2xuYW1lcyh0YWJBZ2UpLGVhY2g9NClbIShhcy5udW1lcmljKHRhYkFnZSkgPT0gMCldLAogIE4uUGFydGljaXBhbnRzID0gYXMubnVtZXJpYyh0YWJBZ2UpWyEoYXMubnVtZXJpYyh0YWJBZ2UpID09IDApXSwKICBDb250ZXh0ID0gcmVwKHJvd25hbWVzKHRhYkFnZSksdGltZXM9MylbIShhcy5udW1lcmljKHRhYkFnZSkgPT0gMCldKQoKCmdncGxvdChnZ2RmLGFlcyh4PUdlbmRlcix5PU4uUGFydGljaXBhbnRzLGZpbGw9Q29udGV4dCkpICsgZ2VvbV9iYXIocG9zaXRpb249ImRvZGdlIixjb2xvdXI9IndoaXRlIixzdGF0PSJpZGVudGl0eSIpICArIGxhYnMoeT0iTiBwYXJ0aWNpcGFudHMiKSArIHNjYWxlX3lfY29udGludW91cyhicmVha3M9c2VxKDAsOTAsMTApLGxpbWl0cz1jKDAsOTApKSArIHRoZW1lX2J3KCkgKyBnZ3RpdGxlKCJQYXJ0aWNpcGFudHMgYnkgZ2VuZGVyIikrICBnZW9tX3RleHQoYWVzKGxhYmVsID0gTi5QYXJ0aWNpcGFudHMpLCBoanVzdD0wLjUsIHZqdXN0PS0wLjI1LCBzaXplID0gMi41LHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKHdpZHRoPTAuOSkpIAoKYGBgCgpgYGB7ciBvcmlnbnNfYnlfY29udGV4dCxtZXNzYWdlPUZBTFNFfQojIGFkZCBudW1iZXJzIG9uIHRoZSBiYXIKdGFiQWdlIDwtIHQodGFibGUoYWxsJG9yaWdpbnMsYWxsJENvbnRleHQpKQpnZ3Bsb3QoYWxsLGFlcyh4PW9yaWdpbnMsZmlsbD1Db250ZXh0KSkgKyBnZW9tX2Jhcihwb3NpdGlvbj0iZG9kZ2UiLGNvbG91cj0id2hpdGUiKSArIGdndGl0bGUoIk9yaWdpbnMgYnkgY29udGV4dCIpICsgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz1zZXEoMCw5MCwxMCksbGltaXRzPWMoMCw5MCkpICsgdGhlbWVfYncoKSArIGRyYXdfZ3JvYih0YWJsZUdyb2IodGFiQWdlKSwgeD0yLCB5PTYwLCB3aWR0aD0wLjMsIGhlaWdodD0wLjQpICsgZ2d0aXRsZSgiUGFydGljaXBhbnRzIGJ5IG9yaWdpbnMiKQp0YWJBZ2UKYGBgCgotIHByb2ZpY2llbmN5CgpgYGB7ciBwcm9maWNpZW5jeV9ieV9jb250ZXh0LG1lc3NhZ2U9RkFMU0V9CnRhYkFnZSA8LSB0KHRhYmxlKGFsbCRwcm9mLGFsbCRDb250ZXh0KSkKZ2dwbG90KGFsbCxhZXMoeD1Db250ZXh0LGZpbGw9cHJvZikpICsgZ2VvbV9iYXIocG9zaXRpb249ImRvZGdlIixjb2xvdXI9IndoaXRlIikgKyBnZ3RpdGxlKCJQcm9maWNpZW5jeSBieSBjb250ZXh0IikgKyBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLDkwLDEwKSxsaW1pdHM9YygwLDkwKSkgKyB0aGVtZV9idygpICsgZHJhd19ncm9iKHRhYmxlR3JvYih0YWJBZ2UpLCB4PTIsIHk9ODAsIHdpZHRoPTAuMywgaGVpZ2h0PTAuNCkKdGFiQWdlCmBgYAoKLSBMMi5WQ0UKCmBgYHtyIEwyVkNFX2J5X2NvbnRleHQsbWVzc2FnZT1GQUxTRX0KdGFiQWdlIDwtIHQodGFibGUoYWxsW2FsbCRDb250ZXh0ICE9ICJFbmdsaXNoIGluIEdlcm1hbnkiICYgYWxsJENvbnRleHQgIT0gIkVuZ2xpc2ggaW4gSXRhbHkiLCJMMi5WQ0UiXSxhbGxbYWxsJENvbnRleHQgIT0gIkVuZ2xpc2ggaW4gR2VybWFueSIgJiBhbGwkQ29udGV4dCAhPSAiRW5nbGlzaCBpbiBJdGFseSIsJ0NvbnRleHQnXSx1c2VOQSA9ICJhbHdheXMiKSkKdGFiQWdlIDwtIHRhYkFnZVstMyxdCgpnZ3Bsb3QoYWxsW2FsbCRDb250ZXh0ICE9ICJFbmdsaXNoIGluIEdlcm1hbnkiICYgYWxsJENvbnRleHQgIT0gIkVuZ2xpc2ggaW4gSXRhbHkiLF0sYWVzKHg9Q29udGV4dCxmaWxsPUwyLlZDRSkpICsgZ2VvbV9iYXIocG9zaXRpb249ImRvZGdlIixjb2xvdXI9IndoaXRlIikgKyBnZ3RpdGxlKCJMMi5WQ0UgYnkgY29udGV4dCIpICsgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz1zZXEoMCw5MCwxMCksbGltaXRzPWMoMCw5MCkpICsgdGhlbWVfYncoKSArIGRyYXdfZ3JvYih0YWJsZUdyb2IodGFiQWdlKSwgeD0yLCB5PTgwLCB3aWR0aD0wLjMsIGhlaWdodD0wLjQpCmBgYAoKLSBkYSBtZXR0ZXJlIGEgcG9zdG8gY29uIFJpY2hpCgpgYGB7ciB5ZWFyLnN0dWR5TDJfRXVyb3BlYW5fY29udGV4dCxtZXNzYWdlPUZBTFNFfQojIHllYXIgc3R1ZHkgTDIKdGFibGUoYWxsJHllYXIuc3R1ZHlMMixhbGwkb3RoZXIueWVhci5zdHVkeUwyLnJpY2hpKQoKYWxsJHllYXIuc3R1ZHlMMiA8LSBpZmVsc2UoYWxsJHllYXIuc3R1ZHlMMiA9PSAiT3RoZXIiLGFsbCRvdGhlci55ZWFyLnN0dWR5TDIucmljaGksYWxsJHllYXIuc3R1ZHlMMiApCgojIEV1cm9wZWFuIGNvbnRleHQKZ2dwbG90KGFsbFthbGwkQ29udGV4dCA9PSAiRW5nbGlzaCBpbiBHZXJtYW55IiB8IGFsbCRDb250ZXh0ID09ICJFbmdsaXNoIGluIEl0YWx5IixdLGFlcyh4PWRlZ3JlZSxmaWxsPXllYXIuc3R1ZHlMMikpICsgZ2VvbV9iYXIocG9zaXRpb249ImRvZGdlIixjb2xvdXI9IndoaXRlIikgKyB0aGVtZV9idygpICsgZ2d0aXRsZSgiRGVncmVlIGJ5IHN0dWR5IHllYXIgTDIsIGJ5IENvbnRleHQiKSArICBmYWNldF9ncmlkKH5Db250ZXh0LHNjYWxlcz0iZnJlZSIpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKyBsYWJzKHkgPSAiTiBwYXJ0aWNpcGFudHMiLCB4ID0gImRlZ3JlZSIpCmBgYAoKLSBEZWdyZWUgb2YgZW5yb2xtZW50CgpgYGB7ciBkZWdyZWVfYnlfY29udGV4dCxtZXNzYWdlPUZBTFNFfQojIEF1c3RyYWxpYW4gY29udGV4dAp0YWJBZ2UgPC0gdCh0YWJsZShhbGxbYWxsJENvbnRleHQgPT0gIkl0YWxpYW4gaW4gQXVzdHJhbGlhIiB8IGFsbCRDb250ZXh0ID09ICJHZXJtYW4gaW4gQXVzdHJhbGlhIiwnZGVncmVlJ10sYWxsW2FsbCRDb250ZXh0ID09ICJJdGFsaWFuIGluIEF1c3RyYWxpYSIgfCBhbGwkQ29udGV4dCA9PSAiR2VybWFuIGluIEF1c3RyYWxpYSIsJ0NvbnRleHQnXSkpCmdncGxvdChhbGxbYWxsJENvbnRleHQgPT0gIkl0YWxpYW4gaW4gQXVzdHJhbGlhIiB8IGFsbCRDb250ZXh0ID09ICJHZXJtYW4gaW4gQXVzdHJhbGlhIixdLGFlcyh4PUNvbnRleHQsZmlsbD1kZWdyZWUpKSArIGdlb21fYmFyKHBvc2l0aW9uPSJkb2RnZSIsY29sb3VyPSJ3aGl0ZSIpICsgdGhlbWVfYncoKSArIGdndGl0bGUoIkRlZ3JlZSBpbiBBdXN0cmFsaWFuIENvbnRleHRzIikgKyBkcmF3X2dyb2IodGFibGVHcm9iKHRhYkFnZSksIHg9MS4sIHk9NDAsIHdpZHRoPTAuMywgaGVpZ2h0PTAuNCkKdGFiQWdlCmBgYAoKYGBge3IgeWVhci5zdHVkeUwyX0F1c3RyYWxpYW5fY29udGV4dCxtZXNzYWdlPUZBTFNFfQojIEF1c3RyYWxpYW4gY29udGV4dAp0YWJBZ2UgPC0gdCh0YWJsZShhbGxbYWxsJENvbnRleHQgPT0gIkVuZ2xpc2ggaW4gSXRhbHkiIHwgYWxsJENvbnRleHQgPT0gIkVuZ2xpc2ggaW4gR2VybWFueSIsJ2RlZ3JlZSddLGFsbFthbGwkQ29udGV4dCA9PSAiRW5nbGlzaCBpbiBJdGFseSIgfCBhbGwkQ29udGV4dCA9PSAiRW5nbGlzaCBpbiBHZXJtYW55IiwnQ29udGV4dCddKSkKCmdncGxvdChhbGxbYWxsJENvbnRleHQgPT0gIkVuZ2xpc2ggaW4gSXRhbHkiIHwgYWxsJENvbnRleHQgPT0gIkVuZ2xpc2ggaW4gR2VybWFueSIsXSxhZXMoeD1Db250ZXh0LGZpbGw9ZGVncmVlKSkgKyBnZW9tX2Jhcihwb3NpdGlvbj0iZG9kZ2UiLGNvbG91cj0id2hpdGUiKSArIHRoZW1lX2J3KCkgKyBnZ3RpdGxlKCJEZWdyZWUgaW4gRXVyb3BlYW4gQ29udGV4dHMiKQoKdGFiQWdlCmBgYAoKXGNsZWFycGFnZQoKIyBMaWtlcnQgc2NhbGVzCgpgYGB7ciBpbmNsdWRlPUZBTFNFLG1lc3NhZ2U9RkFMU0V9Cmxpa2VydF9ncmVwIDwtICJcXC5pZCR8XFwub3VnaHQkfFxcLmludHIkfFxcLmluc3RydSR8XFwuaW50ZWdyJHxcXC5wcm9mJHxcXC5wb3N0JHxcXC5jb21tJHxebmVjZXNzaXR5JHxeZWR1Y2F0ZWQkIgoKIyBhbGwKbGlrZXJ0X3ZhcmlhYmxlc19hbGwgPC0gY29sbmFtZXMoYWxsKVtncmVwKGxpa2VydF9ncmVwLGNvbG5hbWVzKGFsbCkpXQpsaWtlcnRfdmFyaWFibGVzX2FsbApsaWtlcnRfdmFyaWFibGVzX2FsbCA8LSBsaWtlcnRfdmFyaWFibGVzX2FsbFshKGxpa2VydF92YXJpYWJsZXNfYWxsICVpbiUgIm90aGVyLnByb2YiKV0KYGBgCgoKLSBzY2FsYSBkaSBibHVlcyBlIHN0cm9uZ2x5IGRpc2FncmVlIGluIHVuIGNvbG9yZSBjb21wbGV0YW1lbnRlIGRpdmVyc28KCmBgYHtyIGZpZy53aWR0aD0yMCxmaWcuaGVpZ2h0PTIwfQphbGxfbWVsdCA8LSBtZWx0KGFsbCxpZC52YXJzID0gYygiUmVzcC5JRCIsIkdlbmRlciIsIkFnZSIsInByb2YiLCJDb250ZXh0Iiwic3R1ZHkueWVhciIpLAogICAgICAgICAgICAgICAgICAgICAgICBtZWFzdXJlLnZhcnMgPSBsaWtlcnRfdmFyaWFibGVzX2FsbCkKCmFsbF9tZWx0JHZhbHVlIDwtIGZhY3RvcihhbGxfbWVsdCR2YWx1ZSxsZXZlbHM9YygiU3Ryb25nbHkgZGlzYWdyZWUiLCJEaXNhZ3JlZSIsIk5vdCBzdXJlIiwiQWdyZWUiLCJTdHJvbmdseSBhZ3JlZSIpKQoKYWxsX21lbHQgPC0gYWxsX21lbHQgJT4lIHNlcGFyYXRlKHZhcmlhYmxlLGludG89YygiaXRlbSIsInR5cGUiKSxzZXA9IlxcLiIscmVtb3ZlPUZBTFNFKQpnZ3Bsb3QoYWxsX21lbHQsYWVzKHg9dmFyaWFibGUsZmlsbD12YWx1ZSkpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLGNvbG91cj0iYmxhY2siKSArIAogIGZhY2V0X2dyaWQoQ29udGV4dH50eXBlLHNjYWxlcyA9ICJmcmVlIikrdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSxheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCkpICsgZ2d0aXRsZSgiRmlsdGVyZWQgZGF0YXNldCIpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiNjYTAwMjAiLCIjZjRhNTgyIiwiI2ZmZmZiZiIsIiNhYmQ5ZTkiLCIjMmM3YmI2IiwiZ3JleSIpKQoKZmlsdF9zdW0gPC0gYWxsX21lbHQgJT4lIGdyb3VwX2J5KENvbnRleHQsdmFyaWFibGUsdHlwZSx2YWx1ZSkgJT4lIGRwbHlyOjpzdW1tYXJpc2UoTmdyb3VwPWxlbmd0aCh2YWx1ZSkpCmdncGxvdChmaWx0X3N1bSxhZXMoeD12YWx1ZSx5PU5ncm91cCxjb2xvdXI9Q29udGV4dCxncm91cD1pbnRlcmFjdGlvbih2YXJpYWJsZSwgQ29udGV4dCkpKSArIGdlb21fbGluZSgpICsgZ2VvbV9wb2ludCgpICsgZmFjZXRfd3JhcCh+dHlwZSxzY2FsZXMgPSAiZnJlZSIpK3RoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpCgpgYGAKCgotICoqQ29udmVydCBMaWtlcnQgc2NhbGVzIHRvIG51bWJlcnMqKgoKCmBgYHtyIGZpZy53aWR0aD0xNSxmaWcuaGVpZ2h0PTE1fQoKY29udmVydFRvTnVtYmVyIDwtIGZ1bmN0aW9uKGNvbHVtbil7CiAgY29sdW1uIDwtIGZhY3Rvcihjb2x1bW4sbGV2ZWxzID0gYygiU3Ryb25nbHkgZGlzYWdyZWUiLCJEaXNhZ3JlZSIsIk5vdCBzdXJlIiwiQWdyZWUiLCJTdHJvbmdseSBhZ3JlZSIpKQogIGNvbHVtbl9udW1iZXIgPC0gYXMubnVtZXJpYyhjb2x1bW4pCiAgcmV0dXJuKGNvbHVtbl9udW1iZXIpCn0KCnRhYmxlKGFsbCRDb250ZXh0KQoKY29udmVydF9saWtlcnQgPC0gZGF0YS5mcmFtZShhcHBseShzdWJzZXQoYWxsLHNlbGVjdD1saWtlcnRfdmFyaWFibGVzX2FsbCksMixjb252ZXJ0VG9OdW1iZXIpKQpjb2xuYW1lcyhjb252ZXJ0X2xpa2VydCkgPC0gcGFzdGUwKGNvbG5hbWVzKGNvbnZlcnRfbGlrZXJ0KSwiMSIpCgpsaWtlcnRfdmFyaWFibGVzMSA8LSBwYXN0ZTAobGlrZXJ0X3ZhcmlhYmxlc19hbGwsIjEiKQoKIyBqb2luIHRoZSBjb252ZXJ0ZWQgdmFyaWFibGVzIHRvIHRoZSBmaWx0ZXJlZCBkYXRhc2V0CmZpbHRlcmVkX2NvbnYgPC0gY2JpbmQoYWxsLGNvbnZlcnRfbGlrZXJ0KQoKdGFibGUoZmlsdGVyZWRfY29udlssbGlrZXJ0X3ZhcmlhYmxlc19hbGxbNF1dLGZpbHRlcmVkX2NvbnZbLGxpa2VydF92YXJpYWJsZXMxWzRdXSx1c2VOQSA9ICJhbHdheXMiKQoKd3JpdGUuY3N2KGZpbHRlcmVkX2NvbnYsIjAyLWRlc2NyaXB0aXZlX2RhdGEvbWVyZ2VkX2ZpbHRlcmVkX2xpa2VydE51bWJlci5jc3YiLHJvdy5uYW1lcyA9IEZBTFNFKQpgYGAKCiMgQ29ycmVsYXRpb24gcGxvdCBvZiBpdGVtcyBieSBjb250ZXh0CgojIyBJdGFsaWFuIGluIEF1c3RyYWxpYQoKYGBge3IgY29yX2l0YWxpYW5faW5fYXVzdHJhbGlhLGZpZy53aWR0aD0xNSxmaWcuaGVpZ2h0PTE1fQpjb3YgPC0gY29yKGZpbHRlcmVkX2NvbnZbZmlsdGVyZWRfY29udiRDb250ZXh0ID09ICJJdGFsaWFuIGluIEF1c3RyYWxpYSIsbGlrZXJ0X3ZhcmlhYmxlczFbIShsaWtlcnRfdmFyaWFibGVzMSAlaW4lICJuZWNlc3NpdHkxIildXSxtZXRob2QgPSAicGVhcnNvbiIsdXNlPSJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiKQoKCnJvd19pbmZvcyA8LSBkYXRhLmZyYW1lKFZhcmlhYmxlcz1zYXBwbHkoc3Ryc3BsaXQoY29sbmFtZXMoY292KSxzcGxpdD0iXFwuIiksZnVuY3Rpb24oeCkgeFsyXSkpCnJvd19pbmZvcyRWYXJpYWJsZXMgPC0gYXMuY2hhcmFjdGVyKHJvd19pbmZvcyRWYXJpYWJsZXMpCnJvd25hbWVzKHJvd19pbmZvcykgPC0gcm93bmFtZXMoY292KQpyb3dfaW5mb3MkVmFyaWFibGVzW3doaWNoKGlzLm5hKHJvd19pbmZvcyRWYXJpYWJsZXMpKV0gPC0gYygiZWR1Y2F0ZWQiKQpyb3dfaW5mb3MgPC0gcm93X2luZm9zW29yZGVyKHJvd19pbmZvcyRWYXJpYWJsZXMpLCxkcm9wPUZBTFNFXQoKYW5uX2NvbF93aWRlIDwtIGRhdGEuZnJhbWUoVmFyaWFibGU9dW5pcXVlKHJvd19pbmZvcyRWYXJpYWJsZXMpKQphbm5fY29sb3JzX3dpZGUgPC0gbGlzdChWYXJpYWJsZXM9Yyhjb21tMT0iI2JkMDAyNiIsZWR1Y2F0ZWQ9IiNiMzU4MDYiLCBpZDE9IiNmNmU4YzMiLGluc3RydTE9IiMzNTk3OGYiLGludGVncjE9IiMzODZjYjAiLGludHIxPSIjZmZmZjk5IixvdWdodDE9ImdyZXkiLHBvc3QxPSJibGFjayIscHJvZjE9InBpbmsiKSkKCiNwaGVhdG1hcChjb3YsIG1haW4gPSAiSXRhbGlhbiBpbiBBdXN0cmFsaWEiLGFubm90YXRpb25fbmFtZXNfcm93ID0gRkFMU0UsY2x1c3Rlcl9jb2xzPVRSVUUsY2x1c3Rlcl9yb3dzPVRSVUUsYW5ub3RhdGlvbl9jb2wgPSByb3dfaW5mb3NbLDEsZHJvcD1GQUxTRV0sIGFubm90YXRpb25fcm93ID0gcm93X2luZm9zWywxLGRyb3A9RkFMU0VdLCAgYW5ub3RhdGlvbl9jb2xvcnMgPSBhbm5fY29sb3JzX3dpZGUsYnJlYWtzPXNlcSgtMSwxLDAuMiksY29sPWMoIiM2NzAwMWYiLCIjYjIxODJiIiwiI2Q2NjA0ZCIsIiNmNGE1ODIiLCIjZmRkYmM3IiwiI2Y3ZjdmNyIsIiNkMWU1ZjAiLCIjOTJjNWRlIiwiIzQzOTNjMyIsIiMyMTY2YWMiLCIjMDUzMDYxIiksc2hvd19jb2xuYW1lcyA9IEZBTFNFLHdpZHRoID0gNyxoZWlnaHQgPSA3KQojIyMjIyMjIyMjIyMjIyMjIyMjCgpkaWFnKGNvdikgPC0gTkEKcGhlYXRtYXAoY292LCBtYWluID0gIkl0YWxpYW4gaW4gQXVzdHJhbGlhIixhbm5vdGF0aW9uX25hbWVzX3JvdyA9IEZBTFNFLGNsdXN0ZXJfY29scz1UUlVFLGNsdXN0ZXJfcm93cz1UUlVFLGFubm90YXRpb25fY29sID0gcm93X2luZm9zWywxLGRyb3A9RkFMU0VdLCBhbm5vdGF0aW9uX3JvdyA9IHJvd19pbmZvc1ssMSxkcm9wPUZBTFNFXQosICBhbm5vdGF0aW9uX2NvbG9ycyA9IGFubl9jb2xvcnNfd2lkZSxzaG93X2NvbG5hbWVzID0gRkFMU0UsYnJlYWtzID0gc2VxKC0wLjYsMC43LGxlbmd0aC5vdXQgPSA1MCksd2lkdGggPSA3LGhlaWdodCA9IDcsY29sb3I9Y29sb3JSYW1wUGFsZXR0ZShicmV3ZXIucGFsKG4gPSA3LCBuYW1lID0gIlJkQnUiKSkoNTApKQpgYGAKCiMjIEdlcm1hbiBpbiBBdXN0cmFsaWEKCmBgYHtyIGNvcl9nZXJtYW5faW5fYXVzdHJhbGlhLGZpZy53aWR0aD0xNSxmaWcuaGVpZ2h0PTE1fQpjb3YgPC0gY29yKGZpbHRlcmVkX2NvbnZbZmlsdGVyZWRfY29udiRDb250ZXh0ID09ICJHZXJtYW4gaW4gQXVzdHJhbGlhIixsaWtlcnRfdmFyaWFibGVzMVshKGxpa2VydF92YXJpYWJsZXMxICVpbiUgIm5lY2Vzc2l0eTEiKV1dLG1ldGhvZCA9ICJwZWFyc29uIix1c2U9InBhaXJ3aXNlLmNvbXBsZXRlLm9icyIpCgpyb3dfaW5mb3MgPC0gZGF0YS5mcmFtZShWYXJpYWJsZXM9c2FwcGx5KHN0cnNwbGl0KGNvbG5hbWVzKGNvdiksc3BsaXQ9IlxcLiIpLGZ1bmN0aW9uKHgpIHhbMl0pKQpyb3dfaW5mb3MkVmFyaWFibGVzIDwtIGFzLmNoYXJhY3Rlcihyb3dfaW5mb3MkVmFyaWFibGVzKQpyb3duYW1lcyhyb3dfaW5mb3MpIDwtIHJvd25hbWVzKGNvdikKcm93X2luZm9zJFZhcmlhYmxlc1t3aGljaChpcy5uYShyb3dfaW5mb3MkVmFyaWFibGVzKSldIDwtIGMoImVkdWNhdGVkIikKcm93X2luZm9zIDwtIHJvd19pbmZvc1tvcmRlcihyb3dfaW5mb3MkVmFyaWFibGVzKSwsZHJvcD1GQUxTRV0KCmFubl9jb2xfd2lkZSA8LSBkYXRhLmZyYW1lKFZhcmlhYmxlPXVuaXF1ZShyb3dfaW5mb3MkVmFyaWFibGVzKSkKYW5uX2NvbG9yc193aWRlIDwtIGxpc3QoVmFyaWFibGVzPWMoY29tbTE9IiNiZDAwMjYiLGVkdWNhdGVkPSIjYjM1ODA2IiwgaWQxPSIjZjZlOGMzIixpbnN0cnUxPSIjMzU5NzhmIixpbnRlZ3IxPSIjMzg2Y2IwIixpbnRyMT0iI2ZmZmY5OSIsb3VnaHQxPSJncmV5Iixwb3N0MT0iYmxhY2siLHByb2YxPSJwaW5rIikpCgpkaWFnKGNvdikgPC0gTkEKcGhlYXRtYXAoY292LCBtYWluID0gIkdlcm1hbiBpbiBBdXN0cmFsaWEiLGFubm90YXRpb25fbmFtZXNfcm93ID0gRkFMU0UsY2x1c3Rlcl9jb2xzPVRSVUUsY2x1c3Rlcl9yb3dzPVRSVUUsYW5ub3RhdGlvbl9jb2wgPSByb3dfaW5mb3NbLDEsZHJvcD1GQUxTRV0sIGFubm90YXRpb25fcm93ID0gcm93X2luZm9zWywxLGRyb3A9RkFMU0VdCiwgIGFubm90YXRpb25fY29sb3JzID0gYW5uX2NvbG9yc193aWRlLHNob3dfY29sbmFtZXMgPSBGQUxTRSxicmVha3MgPSBzZXEoLTAuNiwwLjcsbGVuZ3RoLm91dCA9IDUwKSx3aWR0aCA9IDcsaGVpZ2h0ID0gNyxjb2xvcj1jb2xvclJhbXBQYWxldHRlKGJyZXdlci5wYWwobiA9IDcsIG5hbWUgPSAiUmRCdSIpKSg1MCkpCmBgYAoKIyMgRW5nbGlzaCBpbiBHZXJtYW55CgpgYGB7ciBjb3JfZW5nbGlzaF9pbl9nZXJtYW55LGZpZy53aWR0aD0xNSxmaWcuaGVpZ2h0PTE1fQpjb3YgPC0gY29yKGZpbHRlcmVkX2NvbnZbZmlsdGVyZWRfY29udiRDb250ZXh0ID09ICJFbmdsaXNoIGluIEdlcm1hbnkiLGxpa2VydF92YXJpYWJsZXMxWyEobGlrZXJ0X3ZhcmlhYmxlczEgJWluJSBjKCJyZWNvbm5lY3QuY29tbTEiLCAgICAic3BlYWtlcnNtZWxiLmNvbW0xIiwiY29tZWNsb3Nlci5jb21tMSIsImVkdWNhdGVkMSIpKV1dLG1ldGhvZCA9ICJwZWFyc29uIix1c2U9InBhaXJ3aXNlLmNvbXBsZXRlLm9icyIpCgpyb3dfaW5mb3MgPC0gZGF0YS5mcmFtZShWYXJpYWJsZXM9c2FwcGx5KHN0cnNwbGl0KGNvbG5hbWVzKGNvdiksc3BsaXQ9IlxcLiIpLGZ1bmN0aW9uKHgpIHhbMl0pKQpyb3dfaW5mb3MkVmFyaWFibGVzIDwtIGFzLmNoYXJhY3Rlcihyb3dfaW5mb3MkVmFyaWFibGVzKQpyb3duYW1lcyhyb3dfaW5mb3MpIDwtIHJvd25hbWVzKGNvdikKcm93X2luZm9zJFZhcmlhYmxlc1t3aGljaChpcy5uYShyb3dfaW5mb3MkVmFyaWFibGVzKSldIDwtIGMoIm5lY2Vzc2l0eSIpCnJvd19pbmZvcyA8LSByb3dfaW5mb3Nbb3JkZXIocm93X2luZm9zJFZhcmlhYmxlcyksLGRyb3A9RkFMU0VdCgphbm5fY29sX3dpZGUgPC0gZGF0YS5mcmFtZShWYXJpYWJsZT11bmlxdWUocm93X2luZm9zJFZhcmlhYmxlcykpCmFubl9jb2xvcnNfd2lkZSA8LSBsaXN0KFZhcmlhYmxlcz1jKGlkMT0iI2Y2ZThjMyIsbmVjZXNzaXR5PSIjYjM1ODA2IixpbnN0cnUxPSIjMzU5NzhmIixpbnRlZ3IxPSIjMzg2Y2IwIixpbnRyMT0iI2ZmZmY5OSIsb3VnaHQxPSJncmV5Iixwb3N0MT0iYmxhY2siLHByb2YxPSJwaW5rIikpCgpkaWFnKGNvdikgPC0gTkEKcGhlYXRtYXAoY292LCBtYWluID0gIkVuZ2xpc2ggaW4gR2VybWFueSIsYW5ub3RhdGlvbl9uYW1lc19yb3cgPSBGQUxTRSxjbHVzdGVyX2NvbHM9VFJVRSxjbHVzdGVyX3Jvd3M9VFJVRSxhbm5vdGF0aW9uX2NvbCA9IHJvd19pbmZvc1ssMSxkcm9wPUZBTFNFXSwgYW5ub3RhdGlvbl9yb3cgPSByb3dfaW5mb3NbLDEsZHJvcD1GQUxTRV0KLCAgYW5ub3RhdGlvbl9jb2xvcnMgPSBhbm5fY29sb3JzX3dpZGUsc2hvd19jb2xuYW1lcyA9IEZBTFNFLGJyZWFrcyA9IHNlcSgtMC42LDAuNyxsZW5ndGgub3V0ID0gNTApLHdpZHRoID0gNyxoZWlnaHQgPSA3LGNvbG9yPWNvbG9yUmFtcFBhbGV0dGUoYnJld2VyLnBhbChuID0gNywgbmFtZSA9ICJSZEJ1IikpKDUwKSkKYGBgCgojIyBFbmdsaXNoIGluIEl0YWx5CgpgYGB7ciBjb3JfZW5nbGlzaF9pbl9pdGFseSxmaWcud2lkdGg9MTUsZmlnLmhlaWdodD0xNX0KY292IDwtIGNvcihmaWx0ZXJlZF9jb252W2ZpbHRlcmVkX2NvbnYkQ29udGV4dCA9PSAiRW5nbGlzaCBpbiBJdGFseSIsbGlrZXJ0X3ZhcmlhYmxlczFbIShsaWtlcnRfdmFyaWFibGVzMSAlaW4lIGMoInJlY29ubmVjdC5jb21tMSIsInNwZWFrZXJzbWVsYi5jb21tMSIsImNvbWVjbG9zZXIuY29tbTEiLCJlZHVjYXRlZDEiKSldXSxtZXRob2QgPSAicGVhcnNvbiIsdXNlPSJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiKQoKcm93X2luZm9zIDwtIGRhdGEuZnJhbWUoVmFyaWFibGVzPXNhcHBseShzdHJzcGxpdChjb2xuYW1lcyhjb3YpLHNwbGl0PSJcXC4iKSxmdW5jdGlvbih4KSB4WzJdKSkKcm93X2luZm9zJFZhcmlhYmxlcyA8LSBhcy5jaGFyYWN0ZXIocm93X2luZm9zJFZhcmlhYmxlcykKcm93bmFtZXMocm93X2luZm9zKSA8LSByb3duYW1lcyhjb3YpCnJvd19pbmZvcyRWYXJpYWJsZXNbd2hpY2goaXMubmEocm93X2luZm9zJFZhcmlhYmxlcykpXSA8LSAibmVjZXNzaXR5Igpyb3dfaW5mb3MgPC0gcm93X2luZm9zW29yZGVyKHJvd19pbmZvcyRWYXJpYWJsZXMpLCxkcm9wPUZBTFNFXQoKYW5uX2NvbF93aWRlIDwtIGRhdGEuZnJhbWUoVmFyaWFibGU9dW5pcXVlKHJvd19pbmZvcyRWYXJpYWJsZXMpKQphbm5fY29sb3JzX3dpZGUgPC0gbGlzdChWYXJpYWJsZXM9Yyhjb21tMT0iI2JkMDAyNiIsbmVjZXNzaXR5PSIjYjM1ODA2IiwgaWQxPSIjZjZlOGMzIixpbnN0cnUxPSIjMzU5NzhmIixpbnRlZ3IxPSIjMzg2Y2IwIixpbnRyMT0iI2ZmZmY5OSIsb3VnaHQxPSJncmV5Iixwb3N0MT0iYmxhY2siLHByb2YxPSJwaW5rIikpCgpkaWFnKGNvdikgPC0gTkEKcGhlYXRtYXAoY292LCBtYWluID0gIkVuZ2xpc2ggaW4gSXRhbHkiLGFubm90YXRpb25fbmFtZXNfcm93ID0gRkFMU0UsY2x1c3Rlcl9jb2xzPVRSVUUsY2x1c3Rlcl9yb3dzPVRSVUUsYW5ub3RhdGlvbl9jb2wgPSByb3dfaW5mb3NbLDEsZHJvcD1GQUxTRV0sIGFubm90YXRpb25fcm93ID0gcm93X2luZm9zWywxLGRyb3A9RkFMU0VdCiwgIGFubm90YXRpb25fY29sb3JzID0gYW5uX2NvbG9yc193aWRlLHNob3dfY29sbmFtZXMgPSBGQUxTRSxicmVha3MgPSBzZXEoLTAuNiwwLjcsbGVuZ3RoLm91dCA9IDUwKSx3aWR0aCA9IDcsaGVpZ2h0ID0gNyxjb2xvcj1jb2xvclJhbXBQYWxldHRlKGJyZXdlci5wYWwobiA9IDcsIG5hbWUgPSAiUmRCdSIpKSg1MCkpCmBgYAoKIyMgQWxsIGNvbnRleHQgdG9nZXRoZXIKCmBgYHtyIGNvcl9hbGxfY29udGV4dHN9CmNvdiA8LSBjb3IoZmlsdGVyZWRfY29udlssbGlrZXJ0X3ZhcmlhYmxlczFdLG1ldGhvZCA9ICJwZWFyc29uIix1c2U9InBhaXJ3aXNlLmNvbXBsZXRlLm9icyIpCgpyb3dfaW5mb3MgPC0gZGF0YS5mcmFtZShWYXJpYWJsZXM9c2FwcGx5KHN0cnNwbGl0KGNvbG5hbWVzKGNvdiksc3BsaXQ9IlxcLiIpLGZ1bmN0aW9uKHgpIHhbMl0pKQpyb3dfaW5mb3MkVmFyaWFibGVzIDwtIGFzLmNoYXJhY3Rlcihyb3dfaW5mb3MkVmFyaWFibGVzKQpyb3duYW1lcyhyb3dfaW5mb3MpIDwtIHJvd25hbWVzKGNvdikKcm93X2luZm9zJFZhcmlhYmxlc1t3aGljaChpcy5uYShyb3dfaW5mb3MkVmFyaWFibGVzKSldIDwtIGMoIm5lY2Vzc2l0eSIsImVkdWNhdGVkIikKcm93X2luZm9zIDwtIHJvd19pbmZvc1tvcmRlcihyb3dfaW5mb3MkVmFyaWFibGVzKSwsZHJvcD1GQUxTRV0KCmFubl9jb2xfd2lkZSA8LSBkYXRhLmZyYW1lKFZhcmlhYmxlPXVuaXF1ZShyb3dfaW5mb3MkVmFyaWFibGVzKSkKYW5uX2NvbG9yc193aWRlIDwtIGxpc3QoVmFyaWFibGVzPWMoY29tbTE9IiNiZDAwMjYiLGVkdWNhdGVkPSJvcmFuZ2UiLCBpZDE9IiNmNmU4YzMiLGluc3RydTE9IiMzNTk3OGYiLG5lY2Vzc2l0eT0iI2IzNTgwNiIsaW50ZWdyMT0iIzM4NmNiMCIsaW50cjE9IiNmZmZmOTkiLG91Z2h0MT0iZ3JleSIscG9zdDE9ImJsYWNrIixwcm9mMT0icGluayIpKQoKZGlhZyhjb3YpIDwtIE5BCnBoZWF0bWFwKGNvdiwgbWFpbiA9ICJBbGwgQ29udGV4dHMiLGFubm90YXRpb25fbmFtZXNfcm93ID0gRkFMU0UsY2x1c3Rlcl9jb2xzPVRSVUUsY2x1c3Rlcl9yb3dzPVRSVUUsYW5ub3RhdGlvbl9jb2wgPSByb3dfaW5mb3NbLDEsZHJvcD1GQUxTRV0sIGFubm90YXRpb25fcm93ID0gcm93X2luZm9zWywxLGRyb3A9RkFMU0VdCiwgIGFubm90YXRpb25fY29sb3JzID0gYW5uX2NvbG9yc193aWRlLHNob3dfY29sbmFtZXMgPSBGQUxTRSxicmVha3MgPSBzZXEoLTAuNiwwLjcsbGVuZ3RoLm91dCA9IDUwKSx3aWR0aCA9IDcsaGVpZ2h0ID0gNyxjb2xvcj1jb2xvclJhbXBQYWxldHRlKGJyZXdlci5wYWwobiA9IDcsIG5hbWUgPSAiUmRCdSIpKSg1MCkpCgpgYGAKCiMgRXZhbHVhdGUgaW50ZXJuYWwgY29uc2lzdGVuY3kgb2Yga25vd24gY29uc3RydWN0cyB3aXRoIGFscGhhCgpgYGB7cn0Kc2V0cyA8LSBsaXN0KGlkLnZhcj1saWtlcnRfdmFyaWFibGVzMVtncmVwKCJcXC5pZDEkIixsaWtlcnRfdmFyaWFibGVzMSldLAogICAgICAgICAgICAgb3VnaHQudmFyPWxpa2VydF92YXJpYWJsZXMxW2dyZXAoIlxcLm91Z2h0MSQiLGxpa2VydF92YXJpYWJsZXMxKV0sCiAgICAgICAgICAgICBpbnRyLnZhcj1saWtlcnRfdmFyaWFibGVzMVtncmVwKCJcXC5pbnRyMSQiLGxpa2VydF92YXJpYWJsZXMxKV0sCiAgICAgICAgICAgICBpbnN0cnUudmFyPWxpa2VydF92YXJpYWJsZXMxW2dyZXAoIlxcLmluc3RydTEkIixsaWtlcnRfdmFyaWFibGVzMSldLAogICAgICAgICAgICAgaW50ZWdyMS52YXI9bGlrZXJ0X3ZhcmlhYmxlczFbZ3JlcCgiXFwuaW50ZWdyMSQiLGxpa2VydF92YXJpYWJsZXMxKV0sCiAgICAgICAgICAgICBwcm9mLnZhcj1saWtlcnRfdmFyaWFibGVzMVtncmVwKCJcXC5wcm9mMSQiLGxpa2VydF92YXJpYWJsZXMxKV0sCiAgICAgICAgICAgICBwb3N0LnZhcj1saWtlcnRfdmFyaWFibGVzMVtncmVwKCJcXC5wb3N0MSQiLGxpa2VydF92YXJpYWJsZXMxKV0sCiAgICAgICAgICAgICBjb21tLnZhcj1saWtlcnRfdmFyaWFibGVzMVtncmVwKCJcXC5jb21tMSQiLGxpa2VydF92YXJpYWJsZXMxKV0pCiAgICAgICAgICAgICAgCgpnZXRfYWxwaGEgPC0gZnVuY3Rpb24oZGF0YU1vdCwKICAgICAgICAgICAgICAgICAgICAgIHZhcj1zZXRzJGlkLnZhcil7CiAgdmFyX2FscGhhIDwtIGFscGhhKGRhdGFNb3RbLHZhcl0pCiAgZGF0YWYgPC0gZGF0YS5mcmFtZShhbHBoYT12YXJfYWxwaGEkdG90YWwsCiAgICAgICAgICAgICAgICAgICAgZHJvcCA9IHZhcl9hbHBoYSRhbHBoYS5kcm9wKQogIHJvd25hbWVzKGRhdGFmKSA8LSByb3duYW1lcyh2YXJfYWxwaGEkYWxwaGEuZHJvcCkKICByZXR1cm4oZGF0YWYpCn0KCiMgIkl0YWxpYW4gaW4gQXVzdHJhbGlhIgppdGFfaW5fYXUgPC0gZG8uY2FsbChyYmluZCxsYXBwbHkoc2V0cyxmdW5jdGlvbih4KSB7CiAgZ2V0X2FscGhhKGRhdGE9ZmlsdGVyZWRfY29udltmaWx0ZXJlZF9jb252JENvbnRleHQgPT0gIkl0YWxpYW4gaW4gQXVzdHJhbGlhIixdLAogICAgICAgICAgICAgICAgICAgICAgdmFyPXgpfSkpCml0YV9pbl9hdSR2YXIgPC0gc2FwcGx5KHN0cnNwbGl0KHJvd25hbWVzKGl0YV9pbl9hdSksc3BsaXQ9IlxcLiIpLGZ1bmN0aW9uKHgpIHhbMV0pIAppdGFfaW5fYXUkdmFyLmZ1bGwgPC0gc2FwcGx5KHN0cnNwbGl0KHJvd25hbWVzKGl0YV9pbl9hdSksc3BsaXQ9IlxcLiIpLGZ1bmN0aW9uKHgpIHhbM10pIAppdGFfaW5fYXUkQ29udGV4dCA8LSAiSXRhbGlhbiBpbiBBdXN0cmFsaWEiCnJvd25hbWVzKGl0YV9pbl9hdSkgPC0gTlVMTAoKIyAiR2VybWFuIGluIEF1c3RyYWxpYSIKZ2VybV9pbl9hdSA8LSBkby5jYWxsKHJiaW5kLGxhcHBseShzZXRzLGZ1bmN0aW9uKHgpIHsKICBnZXRfYWxwaGEoZGF0YT1maWx0ZXJlZF9jb252W2ZpbHRlcmVkX2NvbnYkQ29udGV4dCA9PSAiR2VybWFuIGluIEF1c3RyYWxpYSIsXSwKICAgICAgICAgICAgICAgICAgICAgIHZhcj14KX0pKQpnZXJtX2luX2F1JHZhciA8LSBzYXBwbHkoc3Ryc3BsaXQocm93bmFtZXMoZ2VybV9pbl9hdSksc3BsaXQ9IlxcLiIpLGZ1bmN0aW9uKHgpIHhbMV0pIApnZXJtX2luX2F1JHZhci5mdWxsIDwtIHNhcHBseShzdHJzcGxpdChyb3duYW1lcyhnZXJtX2luX2F1KSxzcGxpdD0iXFwuIiksZnVuY3Rpb24oeCkgeFszXSkgCmdlcm1faW5fYXUkQ29udGV4dCA8LSAiR2VybWFuIGluIEF1c3RyYWxpYSIKcm93bmFtZXMoZ2VybV9pbl9hdSkgPC0gTlVMTAoKIyAiRW5nbGlzaCBpbiBHZXJtYW55IgplbmdfaW5fZ2VybSA8LSBkby5jYWxsKHJiaW5kLGxhcHBseShzZXRzWyEobmFtZXMoc2V0cykgJWluJSAiY29tbS52YXIiKV0sZnVuY3Rpb24oeCkgewogIGdldF9hbHBoYShkYXRhPWZpbHRlcmVkX2NvbnZbZmlsdGVyZWRfY29udiRDb250ZXh0ID09ICJFbmdsaXNoIGluIEdlcm1hbnkiLF0sCiAgICAgICAgICAgICAgICAgICAgICB2YXI9eCl9KSkKCiMgdGhlIG9uZXMgdGhhdCBtYWtlcyBpc3N1ZXMKZ2V0X2FscGhhKGRhdGE9ZmlsdGVyZWRfY29udltmaWx0ZXJlZF9jb252JENvbnRleHQgPT0gIkVuZ2xpc2ggaW4gR2VybWFueSIsXSwKICAgICAgICAgICAgICAgICAgICAgIHZhcj1zZXRzJG91Z2h0LnZhcikKCmVuZ19pbl9nZXJtJHZhciA8LSBzYXBwbHkoc3Ryc3BsaXQocm93bmFtZXMoZW5nX2luX2dlcm0pLHNwbGl0PSJcXC4iKSxmdW5jdGlvbih4KSB4WzFdKSAKZW5nX2luX2dlcm0kdmFyLmZ1bGwgPC0gc2FwcGx5KHN0cnNwbGl0KHJvd25hbWVzKGVuZ19pbl9nZXJtKSxzcGxpdD0iXFwuIiksZnVuY3Rpb24oeCkgeFszXSkgCmVuZ19pbl9nZXJtJENvbnRleHQgPC0gIkVuZ2xpc2ggaW4gR2VybWFueSIKcm93bmFtZXMoZW5nX2luX2dlcm0pIDwtIE5VTEwKCiMgIkVuZ2xpc2ggaW4gSXRhbHkiCmVuZ19pbl9pdGEgPC0gZG8uY2FsbChyYmluZCxsYXBwbHkoc2V0c1shKG5hbWVzKHNldHMpICVpbiUgImNvbW0udmFyIildLGZ1bmN0aW9uKHgpIHsKICBnZXRfYWxwaGEoZGF0YT1maWx0ZXJlZF9jb252W2ZpbHRlcmVkX2NvbnYkQ29udGV4dCA9PSAiRW5nbGlzaCBpbiBJdGFseSIsXSwKICAgICAgICAgICAgICAgICAgICAgIHZhcj14KX0pKQplbmdfaW5faXRhJHZhciA8LSBzYXBwbHkoc3Ryc3BsaXQocm93bmFtZXMoZW5nX2luX2l0YSksc3BsaXQ9IlxcLiIpLGZ1bmN0aW9uKHgpIHhbMV0pIAplbmdfaW5faXRhJHZhci5mdWxsIDwtIHNhcHBseShzdHJzcGxpdChyb3duYW1lcyhlbmdfaW5faXRhKSxzcGxpdD0iXFwuIiksZnVuY3Rpb24oeCkgeFszXSkgCmVuZ19pbl9pdGEkQ29udGV4dCA8LSAiRW5nbGlzaCBpbiBJdGFseSIKcm93bmFtZXMoZW5nX2luX2l0YSkgPC0gTlVMTAoKCiMgY29tYmluZQpmdWxsX2FscGhhIDwtIHJiaW5kKGVuZ19pbl9pdGEsZW5nX2luX2dlcm0sZ2VybV9pbl9hdSxpdGFfaW5fYXUpCgpgYGAKCi0gUGxvdCBhbHBoYSBieSB2YXJpYWJsZQoKYGBge3IgYWxwaGFfY2hyb25iYWNoX2J5X2NvbnRleHR9CgpmdWxsX2FscGhhICU+JSBncm91cF9ieShDb250ZXh0LHZhcikgJT4lIAogIHN1bW1hcmlzZShzdC5hbHBoYSA9IHVuaXF1ZShhbHBoYS5zdGQuYWxwaGEpLAogICAgICAgICAgICBHNj11bmlxdWUoYWxwaGEuRzYuc21jLikpICU+JQogIGdncGxvdCguLGFlcyh4PXZhcix5PXN0LmFscGhhLGNvbG91cj1Db250ZXh0KSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX2xpbmUoYWVzKGdyb3VwPUNvbnRleHQpKSArIHRoZW1lX2J3KCkKCmBgYAoKCmBgYHtyIGZpZy5oZWlnaHQ9MTgsZmlnLndpZHRoPTE0fQphbGxfbWVsdCA8LSBhbGxfbWVsdCAlPiUgc2VwYXJhdGUodmFyaWFibGUsaW50bz1jKCJpdGVtIiwidHlwZSIpLHNlcD0iXFwuIixyZW1vdmU9RkFMU0UpCnAxPWdncGxvdChhbGxfbWVsdCxhZXMoeD12YXJpYWJsZSxmaWxsPXZhbHVlKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIpICsgCiAgZmFjZXRfZ3JpZChDb250ZXh0fnR5cGUsc2NhbGVzID0gImZyZWUiKSArIGdndGl0bGUoIkZpbHRlcmVkIGRhdGFzZXQiKSt0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSkrdGhlbWVfYncoKQoKcDI9Z2dwbG90KGZ1bGxfYWxwaGEsYWVzKHg9dmFyLmZ1bGwseT1kcm9wLnN0ZC5hbHBoYSxjb2xvdXI9Q29udGV4dCkpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9saW5lKGFlcyhncm91cD1Db250ZXh0KSkgKyB0aGVtZV9idygpICsgZmFjZXRfd3JhcCh+dmFyLHNjYWxlcz0iZnJlZSIpCgpwND1nZ3Bsb3QoZnVsbF9hbHBoYSxhZXMoeD12YXIuZnVsbCx5PWRyb3AuYXZlcmFnZV9yLGNvbG91cj1Db250ZXh0KSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX2xpbmUoYWVzKGdyb3VwPUNvbnRleHQpKSArIHRoZW1lX2J3KCkgKyBmYWNldF93cmFwKH52YXIsc2NhbGVzPSJmcmVlIikKCnAzPWZ1bGxfYWxwaGEgJT4lIGdyb3VwX2J5KENvbnRleHQsdmFyKSAlPiUgCiAgc3VtbWFyaXNlKHN0LmFscGhhID0gdW5pcXVlKGFscGhhLnN0ZC5hbHBoYSksCiAgICAgICAgICAgIEc2PXVuaXF1ZShhbHBoYS5HNi5zbWMuKSkgJT4lCiAgZ2dwbG90KC4sYWVzKHg9dmFyLHk9c3QuYWxwaGEsY29sb3VyPUNvbnRleHQpKSArIGdlb21fcG9pbnQoKSArIGdlb21fbGluZShhZXMoZ3JvdXA9Q29udGV4dCkpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSxheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCkpICsgdGhlbWVfYncoKQoKCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwMixwMyxucm93PTIpCgpgYGAKClxjbGVhcnBhZ2UKCiMgRmFjdG9yIEFuYWx5c2lzIHdpdGggUm9iYnkncyBmdW5jdGlvbgoKIyMgUmVhZCBpbiBkYXRhCgpgYGB7cn0KYWxsIDwtIHJlYWQuY3N2KGZpbGUucGF0aCgiMDItZGVzY3JpcHRpdmVfZGF0YS9tZXJnZWRfZmlsdGVyZWRfbGlrZXJ0TnVtYmVyLmNzdiIpKQpgYGAKCiMjIExpa2VydCB2YXJpYWJsZXMKCmBgYHtyIGluY2x1ZGU9RkFMU0UsbWVzc2FnZT1GQUxTRX0KbGlrZXJ0X2dyZXAgPC0gIlxcLmlkJHxcXC5vdWdodCR8XFwuaW50ciR8XFwuaW5zdHJ1JHxcXC5pbnRlZ3IkfFxcLnByb2YkfFxcLnBvc3QkfFxcLmNvbW0kfF5uZWNlc3NpdHkkfF5lZHVjYXRlZCQiCgojIGFsbApsaWtlcnRfdmFyaWFibGVzX2FsbCA8LSBjb2xuYW1lcyhhbGwpW2dyZXAobGlrZXJ0X2dyZXAsY29sbmFtZXMoYWxsKSldCmxpa2VydF92YXJpYWJsZXNfYWxsCmxpa2VydF92YXJpYWJsZXNfYWxsIDwtIGxpa2VydF92YXJpYWJsZXNfYWxsWyEobGlrZXJ0X3ZhcmlhYmxlc19hbGwgJWluJSAib3RoZXIucHJvZiIpXQoKIyBsaWtlcnQgdmFyaWFibGVzIGNvbnZlcnRlZCB0byBudW1iZXJzCmxpa2VydF92YXJpYWJsZXMxIDwtIHBhc3RlMChsaWtlcnRfdmFyaWFibGVzX2FsbCwiMSIpCgpgYGAKCiMjIEFscGhhIGFuZCBGQSB3aXRoIHRoZSBjb21iaW5lZCBkYXRhc2V0CgpgYGB7cn0KZGF0IDwtIGFsbFssbGlrZXJ0X3ZhcmlhYmxlczFbIShsaWtlcnRfdmFyaWFibGVzMSAlaW4lIGMoIm5lY2Vzc2l0eTEiLCJlZHVjYXRlZDEiKSldXQpwc3ljaDo6YWxwaGEoZGF0LHVzZT0icGFpcndpc2UuY29tcGxldGUub2JzIikKCiNkZXRhY2goInBhY2thZ2U6Z2dwbG90MiIsIHVubG9hZD1UUlVFKQoKCmZhX2FsbCA8LSBmdW5jdGlvbihkYXRhNCxyb3QsbWluTCxtYXhMLG5mYWM9MCxzZWVkPTUpewogIAogIHNldC5zZWVkKHNlZWQpCiAgCiAgIyBTYXZlIHRoZSBvcmlnbmFsIGRhdGFzZXQKICBkYXRhX29yaWcgPC0gZGF0YTQKICAKICAjIERlZmluZSB0aGUgcm90YXRpb25zCiAgb3J0aCA8LSBjKCJ2YXJpbWF4IiwgInF1YXJ0aW1heCIsICJiZW50bGVyVCIsICJlcXVhbWF4IiwgInZhcmltaW4iLCAiZ2VvbWluVCIgLCAiYmlmYWN0b3IiICkKICBvYmwgPC0gYygiUHJvbWF4IiwgInByb21heCIsICJvYmxpbWluIiwgInNpbXBsaW1heCIsICJiZW50bGVyUSIsICJnZW9taW5RIiwgImJpcXVhcnRpbWluIiAsImNsdXN0ZXIiKQogIAogIAogIAogIGFscGhhMiA8LSBmdW5jdGlvbih4KXsKICAgIAogICAgYWxwaGEuMSA8LSBmdW5jdGlvbihDLCBSKSB7CiAgICAgIG4gPC0gZGltKEMpWzJdCiAgICAgIGFscGhhLnJhdyA8LSAoMSAtIHRyKEMpL3N1bShDKSkgKiAobi8obiAtIDEpKQogICAgICBzdW1SIDwtIHN1bShSKQogICAgICBhbHBoYS5zdGQgPC0gKDEgLSBuL3N1bVIpICogKG4vKG4gLSAxKSkKICAgICAgc21jLlIgPC0gc21jKFIpCiAgICAgIEc2IDwtICgxIC0gKG4gLSBzdW0oc21jLlIpKS9zdW1SKQogICAgICBhdi5yIDwtIChzdW1SIC0gbikvKG4gKiAobiAtIDEpKQogICAgICBtb2QxIDwtIG1hdHJpeChhdi5yLCBuLCBuKQogICAgICBSZXMxIDwtIFIgLSBtb2QxCiAgICAgIEdGMSA9IDEgLSBzdW0oUmVzMV4yKS9zdW0oUl4yKQogICAgICBSZCA8LSBSIC0gZGlhZyhSKQogICAgICBkaWFnKFJlczEpIDwtIDAKICAgICAgR0YxLm9mZiA8LSAxIC0gc3VtKFJlczFeMikvc3VtKFJkXjIpCiAgICAgIHNuIDwtIG4gKiBhdi5yLygxIC0gYXYucikKICAgICAgUSA9ICgyICogbl4yLygobiAtIDEpXjIgKiAoc3VtKEMpXjMpKSkgKiAoc3VtKEMpICogKHRyKEMgJSolIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDKSArICh0cihDKSleMikgLSAyICogKHRyKEMpICogc3VtKEMgJSolIEMpKSkKICAgICAgcmVzdWx0IDwtIGxpc3QocmF3ID0gYWxwaGEucmF3LCBzdGQgPSBhbHBoYS5zdGQsIEc2ID0gRzYsIAogICAgICAgICAgICAgICAgICAgICBhdi5yID0gYXYuciwgc24gPSBzbiwgUSA9IFEsIEdGMSwgR0YxLm9mZikKICAgICAgcmV0dXJuKHJlc3VsdCkKICAgIH0KICAgIAogICAgCiAgICBpZiAoIWlzQ29ycmVsYXRpb24oeCkpIHsKICAgICAgaXRlbS52YXIgPC0gYXBwbHkoeCwgMiwgc2QsIG5hLnJtID0gVCkKICAgICAgYmFkIDwtIHdoaWNoKChpdGVtLnZhciA8PSAwKSB8IGlzLm5hKGl0ZW0udmFyKSkKICAgICAgaWYgKChsZW5ndGgoYmFkKSA+IDApICYmIGRlbGV0ZSkgewogICAgICAgIGZvciAoYmFkZHkgaW4gMTpsZW5ndGgoYmFkKSkgewogICAgICAgICAgd2FybmluZygiSXRlbSA9ICIsIGNvbG5hbWVzKHgpW2JhZF1bYmFkZHldLCAiIGhhZCBubyB2YXJpYW5jZSBhbmQgd2FzIGRlbGV0ZWQiKQogICAgICAgIH0KICAgICAgICB4IDwtIHhbLCAtYmFkXQogICAgICAgIG52YXIgPC0gbnZhciAtIGxlbmd0aChiYWQpCiAgICAgIH0KICAgICAgcmVzcG9uc2UuZnJlcSA8LSByZXNwb25zZS5mcmVxdWVuY2llcyh4LCBtYXggPSAxMCkKICAgICAgQyA8LSBjb3YoeCwgdXNlID0gInBhaXJ3aXNlIikKICAgIH0KICAgIGVsc2UgewogICAgICBDIDwtIHgKICAgIH0KICAgIAogICAgCiAgICAKICAgIFIgPC0gY292MmNvcihDKQogICAgCiAgICBhbHBoYS50b3RhbCA8LSBhbHBoYS4xKEMsIFIpCiAgICAKICAgIHJldHVybihhbHBoYS50b3RhbCkKICB9CiAgCiAgCiAgY2ljbDwtMAogIHBhcjE8LTEKICBwYXIyPC0xCiAgcGFyMzwtMQogIHdoaWxlKHBhcjM+MCl7CiAgICAKICAgIAogICAgIyBTZWxlemlvbmUgaWwgbnVtZXJvIGRpIGZhdHRvcmkgY29uc2lnbGlhdGkKICAgIAogICAgY2F0KCJDYWxjdWxhdGluZyB0aGUgbnVtYmVyIG9mIGZhY3RvcnMgbmVlZGVkIFxuIikKICAgIAogICAgZmFjdCA8LSBuZmFjCiAgICBpZihuZmFjPT0wKXsKICAgICAgZmFjdDwtZmEucGFyYWxsZWwoZGF0YTQpJG5mYWN0CiAgICB9CiAgICAKICAgIGlmKGZhY3Q9PTEpewogICAgICBzdG9wKCJPbmx5IG9uZSBmYWN0b3IgcmVtYWlucy4gQ2hlY2sgdGhlIGRhdGEgb3IgcmVkdWNlIHRoZSB0aHJlc2hvbGQiKQogICAgfQogICAgCiAgICAjcm90PC1jKCJub25lIiwgInZhcmltYXgiLCAicXVhcnRpbWF4IiwgImJlbnRsZXJUIiwgImdlb21pblQiICwgImJpZmFjdG9yIiwgInByb21heCIsICJvYmxpbWluIiwgInNpbXBsaW1heCIsICJiZW50bGVyUSIsICJnZW9taW5RIiAsICJiaXF1YXJ0aW1pbiIgLCAiY2x1c3RlciIgKQogICAgCiAgICAjIE1hdHJpY2UgdW90YSBjaGUgY29udGVycmEgbGUgbWllZGllIGRlZ2xpIGFscGhhCiAgICAKICAgIHJhbmdlIDwtIGxlbmd0aChzZXEobWluTCxtYXhMLDAuMDEpKQogICAgCiAgICAKICAgIG1lYW4uYWw8LW1hdHJpeCgwLHJhbmdlLGxlbmd0aChyb3QpKQogICAgCiAgICAjIFNvdHRvY2ljbG8gcGVyIGlsIGNhbGNvbG8gZGVsbGUgbWVkaWUgZGVnbGkgYWxwaGEgKHN1aSBmYXR0b3JpKSBhbCB2YXJpYXJlIHNpYSBkZWxsYSBzb2dsaWEgc2lhIGRlbGxhIGRlbGxhIHJvdGF6aW9uZQogICAgCiAgICBjYXQoIkNhbGN1bGF0aW5nIHRoZSBsb2FkaW5ncyB0aHJlc2hvbGRzIFxuIikKICAgIAogICAgZm9yKGIgaW4gMTpsZW5ndGgocm90KSl7CiAgICAgIGNhdCgiLSBDYWxjdWxhdGluZyB0aGUgbG9hZGluZ3MgdGhyZXNob2xkcyBmb3Igcm90YXRpb24iLHJvdFtiXSwiXG4iKQogICAgICBmYTE8LWZhKGRhdGE0LGZhY3Qscm90YXRlPXJvdFtiXSkKICAgICAgCiAgICAgIGE8LWZhMSRsb2FkaW5ncwogICAgICBjbGFzcyhhKTwtIm1hdHJpeCIKICAgICAgY29sbmFtZXMoYSk8LXBhc3RlKCJGIiwxOmZhY3Qsc2VwPSIiKQogICAgICBhPC1hcy5kYXRhLmZyYW1lKGEpCiAgICAgIGE8LXJvdW5kKGEsMikKICAgICAgYSREPC1yb3duYW1lcyhhKQogICAgICAKICAgICAgbHMxPC1taW5MCiAgICAgIG08LXJlcCgwLHJhbmdlKQogICAgICBpPC0xCiAgICAgIG52PC1mYWN0CiAgICAgIAogICAgICB3aGlsZShsczE8PW1heEwrMC4wMSl7CiAgICAgICAgCiAgICAgICAgdmFyPC1sYXBwbHkoMTpudixmdW5jdGlvbihmKXVuaXF1ZShhJERbYWJzKGFbLGZdKT5sczFdKSkKICAgICAgICBuYW1lcyh2YXIpPC1wYXN0ZShjb2xuYW1lcyhhWywxOm52XSkpCiAgICAgICAgCiAgICAgICAgCiAgICAgICAgIyBEbyB0aGlzIGlmIHRoZXJlIGFyZSBmYWN0b3JzIHdpdGggbGVuZ3RoIGxlc3MgdGhhbiAyCiAgICAgICAgaWYoYW55KGFzLm51bWVyaWMoc3VtbWFyeSh2YXIpWywxXSk8MikpewogICAgICAgICAgCiAgICAgICAgICAjIElmIGEgY2VydGFpbiB0aHJlc2hvbGQgYW5kIHJvdGF0aW9uIGdpdmVzIG9ubHkgZmFjdG9ycyBjb21wb3NlZCBieSAxIGdpdmUgaXQgYWxwaGE9MAogICAgICAgICAgaWYoc3VtKGFzLm51bWVyaWMoc3VtbWFyeSh2YXIpWywxXSk+MSk9PTApewogICAgICAgICAgICBtW2ldIDwtIDAKICAgICAgICAgIH1lbHNlewogICAgICAgICAgICB2YXIxPC12YXJbYXMubnVtZXJpYyhzdW1tYXJ5KHZhcilbLDFdKT4xXQogICAgICAgICAgICBhbDwtc2FwcGx5KDE6bGVuZ3RoKHZhcjEpLGZ1bmN0aW9uKHYpYWxwaGEyKGRhdGE0Wyx2YXIxW1t2XV1dKSRzdGQpCiAgICAgICAgICAgIGFsPC1kYXRhLmZyYW1lKGFsKQogICAgICAgICAgICAjbVtpXTwtbWVhbih0KGFsKSkKICAgICAgICAgICAgbVtpXTwtbWVkaWFuKHQoYWwpKQogICAgICAgICAgfQogICAgICAgICAgCiAgICAgICAgfQogICAgICAgICMgRG8gdGhpcyBpZiBBTEwgdGhlIGZhY3RvcnMgaGF2ZSBsZW5ndGggZ3JlYXRlciB0aGFuIDEKICAgICAgICBlbHNlewogICAgICAgICAgYWw8LXNhcHBseSgxOm52LGZ1bmN0aW9uKHYpYWxwaGEyKGRhdGE0Wyx2YXJbW3ZdXV0pJHN0ZCkKICAgICAgICAgIGFsPC1kYXRhLmZyYW1lKGFsKQogICAgICAgICAgI21baV08LW1lYW4odChhbCkpCiAgICAgICAgICBtW2ldPC1tZWRpYW4odChhbCkpCiAgICAgICAgfQogICAgICAgIAogICAgICAgIGk8LWkrMQogICAgICAgIGxzMTwtbHMxKzAuMDEKICAgICAgICAjY2F0KGxzMSkKICAgICAgfQogICAgICBtZWFuLmFsWyxiXTwtbQogICAgICAjY2F0KCJcbiIpCiAgICB9CiAgICAKICAgICMgQ3JlYXppb25lIGUgc3RhbXBhIGRlZ2xpIGFuZGFtZW50aSBkZWxsZSBtZWRpZSBkZWdsaSBhbHBoYQogICAgY2F0KCJQcm9kdWNpbmcgdGhlIHRocmVzaG9sZCBwbG90IFxuIikKICAgIAogICAgbWVhbi5hbDwtYXMuZGF0YS5mcmFtZShtZWFuLmFsKQogICAgY29sbmFtZXMobWVhbi5hbCk8LXJvdAogICAgbWVhbi5hbCRzbDwtc2VxKG1pbkwsbWF4TCwwLjAxKQogICAgbWVhbi5hbDE8LW1lbHQobWVhbi5hbCxpZC52YXJzPSJzbCIpCiAgICB6cDwtZ2dwbG90KG1lYW4uYWwxLGFlcyh4PXNsLHk9dmFsdWUsY29sb3VyPXZhcmlhYmxlKSkrZ2VvbV9saW5lKCkrbGFicyh4PSJTb2dsaWEgTG9hZGluZyIseT0iQWxwaGEgTWVkaW8iLGNvbG91cj0iUm90YXppb25lIikKICAgIAogICAgCiAgICAjIERpc3BsYXkgdGhlIHBsb3QKICAgIHByaW50KHpwKQogICAgCiAgICBtYXgobWVhbi5hbCkKICAgICNwcmludChtZWFuLmFsKQogICAgCiAgICAjIFNlbGV6aW9uZSBkZWxsYSByb3RhemlvbmUgZSBkZWxsYSBzb2dsaWEgY2hlIG1hc3NpbWl6emFubyBsZSBtZWRpZSBkZWdsaSBhbHBoYQogICAgY2F0KCJDaG9vc2luZyB0aGUgYmVzdCByb3RhdGlvbiBhbmQgdGhyZXNob2xkIFxuIikKICAgIGluZDwtd2hpY2gobWVhbi5hbD09bWF4KG1lYW4uYWwpLGFyci5pbmQ9VCkKICAgIGlmKGNsYXNzKGluZCk9PSJtYXRyaXgiKXsKICAgICAgaW5kPC1pbmRbMSxdCiAgICB9CiAgICAKICAgIHJvdGE8LW5hbWVzKG1lYW4uYWwpW2luZFsyXV0KICAgIHNvZ2w8LW1lYW4uYWwkc2xbaW5kWzFdXQogICAgCiAgICAKICAgICMgS2VlcCB0aGUgc2FtZSByb3RhdGlvbiB0aGF0IHdhcyBzZWxlY3RlZCBpbiB0aGUgZmlyc3QgcnVuCiAgICByb3QgPC0gcm90YQogICAgCiAgICAKICAgICMgQ2FsY29sbyBmYXR0b3JpYWxlCiAgICAKICAgIGZhMTwtZmEoZGF0YTQsZmFjdCxyb3RhdGU9cm90YSkKICAgIGE8LWZhMSRsb2FkaW5ncwogICAgY2xhc3MoYSk8LSJtYXRyaXgiCiAgICBjb2xuYW1lcyhhKTwtcGFzdGUoIkYiLDE6ZmFjdCxzZXA9IiIpCiAgICBhPC1hcy5kYXRhLmZyYW1lKGEpCiAgICBhPC1yb3VuZChhLDIpCiAgICBhJEQ8LXJvd25hbWVzKGEpCiAgICAKICAgICMgQ3JlYXppb25lIGRlaSBmYXR0b3JpCiAgICAKICAgIHZhcjwtbGFwcGx5KDE6ZmFjdCxmdW5jdGlvbihmKXVuaXF1ZShhJERbYWJzKGFbLGZdKT5zb2dsXSkpCiAgICBuYW1lcyh2YXIpPC1wYXN0ZShjb2xuYW1lcyhhWywxOmZhY3RdKSkKICAgIAogICAgIyBEaXNwbGF5IHRoZSBmYWN0b3JzCiAgICBjYXQoIkRpc3BsYXlpbmcgdGhlIGZhY3RvcnMgXG4iKQogICAgcHJpbnQodmFyKQogICAgCiAgICAjIHRvZ2xpYW1vIGxlIHZhcmlhYmlsaSBjaGUgbm9uIGVudHJhbm8gbmVpIGZhdHRvcmkgYWwgc2Vjb25kbyBjaWNsbwogICAgCiAgICBuYzwtIG5jb2woZGF0YTQpCiAgICBwYXIxX25hbWVzIDwtIG5hbWVzKGRhdGE0KVshKG5hbWVzKGRhdGE0KSAlaW4lIHVubGlzdCh2YXIpKV0KICAgIGRhdGE0PC1kYXRhNFssbmFtZXMoZGF0YTQpICVpbiUgdW5saXN0KHZhcildCiAgICAKICAgICMgQWdnaW9ybmlhbW8gaWwgcGFyYW1ldHJvIGRpIGNpY2xvCiAgICAKICAgIHBhcjE8LW5jLW5jb2woZGF0YTQpIAogICAgCiAgICAjIHRvZ2xpYW1vIGxlIHZhcmlhYmlsaSBjaGUgZW50cmFubyBpbiB1biBmYXR0b3JlIGRhIHNvbGUgKHNvbG8gc2Ugbm9uIGVudHJhbm8gaW4gdW4gYWx0cm8gZmF0dG9yZSkKICAgIHBhcjQgPC0gMAogICAgcGFyNF9uYW1lcyA8LSBjaGFyYWN0ZXIoKQogICAgCiAgICBsZW5fZmFjIDwtIGNiaW5kKDE6ZmFjdCxzYXBwbHkoMTpmYWN0LGZ1bmN0aW9uKHYpbGVuZ3RoKHZhcltbdl1dKSkpCiAgICAKICAgIGlmKGFueShsZW5fZmFjWywyXT09MSkpewogICAgICBwYXI0X25hbTwtIGRhdGEuZnJhbWUodmFyaWFibGU9YXMuY2hhcmFjdGVyKHVubGlzdCh2YXJbIGMobGVuX2ZhY1tsZW5fZmFjWywyXT09MSwxXSkgIF0pKSkKICAgICAgcGFyNF9uYW0kdmFyaWFibGUgPC0gYXMuY2hhcmFjdGVyKHBhcjRfbmFtJHZhcmlhYmxlKQogICAgICBwYXI0X25hbSRudGltZXM9IHNhcHBseSgxOm5yb3cocGFyNF9uYW0pLCBmdW5jdGlvbih2KXN1bSh1bmxpc3QodmFyKSVpbiVwYXI0X25hbVt2LCJ2YXJpYWJsZSJdICkgKQogICAgICAjIERvIHRoaXMgb25seSBpZiB0aGUgc2luZ2xlIHZhcmlhYmxlIGVuZXRycyBpbiBhIHZhcmlhYmxlIGFsb25lCiAgICAgIAogICAgICBpZihhbnkocGFyNF9uYW0kbnRpbWVzPjEpICYgcm90JWluJW9ibCApewogICAgICAgIGNhdCgiLSBXaWxsIE5PVCByZW1vdmUgdmFyaWFibGUocykiLCBwYXI0X25hbSR2YXJpYWJsZVtwYXI0X25hbSRudGltZXM+MV0sInNpbmNlIHRoZXkgY29udHJpYnV0ZSB0byBtdWx0aXBsZSBmYWN0b3JzIiwiXG4iKQogICAgICB9CiAgICAgIAogICAgICBwYXI0X25hbWVzIDwtIHBhcjRfbmFtJHZhcmlhYmxlW3BhcjRfbmFtJG50aW1lcz09MV0KICAgICAgcGFyNCA8LSBsZW5ndGgocGFyNF9uYW1lcykKICAgICAgZGF0YTQ8LWRhdGE0WywhbmFtZXMoZGF0YTQpICVpbiUgcGFyNF9uYW1lc10KICAgIH0KICAgIAogICAgCiAgICAjIFRvZ2xpYW1vIGxlIHZhcmlhYmlsaSBjaGUgY29tcGFpb25vIGluIHBpdSBmYXR0b3JpIChzZSBsYSByb3RhemlvbmUgZSBvYmxpcXVhIG5vbiBlZmZldHVpYW1vIHRhbGUgb3BlcmF6aW9uZSkKICAgIAogICAgaWYocm90YSVpbiVvYmwpewogICAgICBwYXIyPC0wCiAgICAgIHMgPC0gY2hhcmFjdGVyKCkKICAgIH1lbHNlewogICAgICBzPC1jKGFzLnZlY3Rvcih1bmxpc3QodmFyKSkpCiAgICAgIHM8LXVuaXF1ZShzW2R1cGxpY2F0ZWQocyldKQogICAgICBwYXIyPC1sZW5ndGgocykKICAgICAgZGF0YTQ8LWRhdGE0WywhbmFtZXMoZGF0YTQpICVpbiUgc10KICAgIH0KICAgIAogICAgCiAgICAKICAgIAogICAgY2ljbDwtY2ljbCsxCiAgICAKICAgICMgQWdnaW9ybmlhbW8gaWwgcGFyYW1ldHJvIGRpIGNpY2xvCiAgICAKICAgIHBhcjM8LXBhcjErcGFyMitwYXI0CiAgICAKICAgICMgU3RhbXBhIGRpYWdub3NpIGNpY2xvCiAgICAKICAgIGNhdChwYXN0ZSgiVW51c2VkIFZhcmlhYmxlOiIscGFyMSksIlxuIikKICAgIGNhdCgiLSIscGFzdGUocGFyMV9uYW1lcyksIlxuIikKICAgIGNhdChwYXN0ZSgiUmVwZWF0ZWQgdmFyaWFibGVzOiIscGFyMiksIlxuIikKICAgIGNhdCgiLSIscGFzdGUocyksIlxuIikKICAgIGNhdChwYXN0ZSgiU2luZ2xlIHZhcmlhYmxlczoiLHBhcjQpLCJcbiIpCiAgICBjYXQoIi0iLHBhc3RlKHBhcjRfbmFtZXMpLCJcbiIpCiAgICBjYXQocGFzdGUoIlJvdGF0aW9uIHVzZWQ6Iixyb3RhKSwiXG4iKQogICAgY2F0KHBhc3RlKCJUaHJlc2hvbGQgY2hvc2VuOiIsc29nbCksIlxuIikKICAgIGNhdChwYXN0ZSgiRW5kIGludGVyYXRpb24iLGNpY2wpLCJcbiIsIlxuIikKICAgIAogIH0KICAKICBjYXQocGFzdGUoIlZhcmlhYmxlcyBleGNsdWRlZCBpbiB0aGUgcHJvY2VzczoiKSxuYW1lcyhkYXRhX29yaWcpWyFuYW1lcyhkYXRhX29yaWcpJWluJW5hbWVzKGRhdGE0KV0sIlxuIikKICByZXR1cm4oZGF0YTQpCn0KCgoKIwoKbGlrZXJ0X3ZhcmlhYmxlczIgPC0gbmFtZXMoZGF0KQoKZGF0YTEgPC0gZGF0WyxsaWtlcnRfdmFyaWFibGVzMVshKGxpa2VydF92YXJpYWJsZXMxICVpbiUgYygibmVjZXNzaXR5MSIsImVkdWNhdGVkMSIsInJlY29ubmVjdC5jb21tMSIsICJzcGVha2Vyc21lbGIuY29tbTEiLCAiY29tZWNsb3Nlci5jb21tMSIpKV1dCgojIFBsb3QgdGhlIGNvcnJlbGF0aW9ucwpjb3JycGxvdChjb3IoZGF0YTEsdXNlID0gInBhaXIiKSkKCgojIGNoZWNrIHdoaWNoIHZhcmlhYmxlIGRvZXMgbm90IGNvcnJlbGF0ZWQgd2l0aCBhbnkgb3RoZXIgdmFpYWJsZQpyMSA8LSBjb3IoZGF0YTEsdXNlID0gInBhaXIiKQpkaWFnKHIxKSA8LSAwCnIxIDwtIGRhdGEuZnJhbWUocjEpCnRlbXAgPC0gZGF0YS5mcmFtZShuYW1lPW5hbWVzKHIxKSxjb3I9c2FwcGx5KDE6bmNvbChyMSksZnVuY3Rpb24odilhbnkocjFbLHZdPj0wLjMpKSAgKQphcy5jaGFyYWN0ZXIodGVtcCRuYW1lW3RlbXAkY29yPT1GXSkKCgoKCiMgS2VlcCBqdXN0IHRoZSBpdGVtZXMgd2l0aCBhIHIuY29yIGdyZWF0ZXIgb3IgZXF1YWwgdG8gMC4zCmFzIDwtIHBzeWNoOjphbHBoYShkYXRhMSxjaGVjay5rZXlzPUYpJGl0ZW0uc3RhdHMKYXMkbjE8LTE6bnJvdyhhcykKc3VtbWFyeShhcyRyLmNvcikKbm9fY29ycl9tYWN1IDwtIHJvd25hbWVzKGFzW2FicyhhcyRyLmNvcik8MC4zLF0pCm5vX2NvcnJfbWFjdQpkYXRhMTwtZGF0YTFbLCFuYW1lcyhkYXRhMSklaW4lbm9fY29ycl9tYWN1XQoKCgoKIyBjaGVjayB3aGljaCBpdGVtcyB3aWxsIGRlY3JlYXNlIHRoZSBhbHBoYQphbDwtIHBzeWNoOjphbHBoYShkYXRhMSkKZHJvcDwtYWwkYWxwaGEuZHJvcAp0b3Q8LWFzLm51bWVyaWMoYWwkdG90YWwkc3RkLmFscGhhKQpkcm9wIDwtIGRyb3BbZHJvcCRzdGQuYWxwaGE+dG90LF0KZHJvcAoKCiMgQ2hlY2sgaG93IG11Y2gKZHJvcCRzdGQuYWxwaGEtdG90CiMgVGhleSBkb247dCBkcm9wIGVub3VnaCBzbyBsZWF2ZSBpdAoKI2Ryb3BfYWxwaGFfbWFjdSA8LSByb3duYW1lcyhkcm9wKQojZGF0YTE8LWRhdGExWywhbmFtZXMoZGF0YTEpJWluJWRyb3BfYWxwaGFfbWFjdV0KCgoKCiMgY2hlY2sgaG93IG1hbnkgZmFjdG9ycyBzaG91bGQgYmUgdXNlZApmYXAgPC0gZmEucGFyYWxsZWwoZGF0YTEpCmZhcAoKCmRhdGFfbWFjdSA8LSBkYXRhMQoKCiMjIFBlcmZvcm0gdGhlIGFubGF5c2lzIGZvciBtYWN1bGFyIHRoaWNrbmVzcwoKCgojQ2hlY2sgYWdhaW4gdGhlIG91dF9tYWN1bGllcnMKb3V0X21hY3UgPC0gb3V0bGllcihkYXRhX21hY3UpCiNhYmxpbmUoaD04MDApCgojZGF0X2ltcF9waGVub1tvdXRfbWFjdT44MDAsMToyMF0KI291dF9tYWN1bGllcnNfbWFjdSA8LSBkYXRfaW1wX3BoZW5vJGlkMVtvdXRfbWFjdT44MDBdCiNkYXRhX21hY3UgPC0gZGF0YV9tYWN1W291dF9tYWN1PDgwMCxdCgoKCgojIFJ1biB0aGUgZmlyc3Qgc2V0IG9mIGFuYWx5c2lzaSBhbmQgY2hlY2sgd2hhdCBpcyBjbGVhbmVkIG91dF9tYWN1IQpyb3Q9Yygib2JsaW1pbiIsInByb21heCIpCgoKIyBUYWtlIG9mZiB0aGUgdmFyaWFibGVzIHRoYXQgZ2l2ZSBwcm9ibGVtCiNwcm9ibGVtX3Zhcl9tYWN1IDwtIGMoICJ2MDdfc3BlY3RyYWxpcyIgLCJ2MDFfY3lycnVzIiApCiNkYXRhX21hY3UgPC0gZGF0YV9tYWN1WywhbmFtZXMoZGF0YV9tYWN1KSVpbiVwcm9ibGVtX3Zhcl9tYWN1XQoKCmxpYnJhcnkoZ2dwbG90MikKZGF0YV9tYWN1X2ZhY2xlYW5lZCA8LSBmYV9hbGwoZGF0YV9tYWN1LHJvdCwwLjIsMC41KQoKIyAwIFZhcmlhYmxlIGRvIG5vdCBlbnRlciB0aGUgZmFjdG9ycwpub3RfdXNlZF9mYWN0X21hY3UgPC0gbmFtZXMoZGF0YV9tYWN1KVshbmFtZXMoZGF0YV9tYWN1KSVpbiVuYW1lcyhkYXRhX21hY3VfZmFjbGVhbmVkKV0Kbm90X3VzZWRfZmFjdF9tYWN1CgojIENoZWNrIGFnYWluIGhvdyBtYW55IGZhY3RvcnMgeW91IG5lZWQKI2ZhLnBhcmFsbGVsKGRhdGFfbWFjdV9mYWNsZWFuZWQpCgpuZmFjdF9tYWN1IDwtIDYKCiMgUnVuIHRoZSBhY3R1YWwgZmFjdG9yaWFsIGFuYWx5c2lzIG9uIHRoZSBmaW5hbCBkYXRhc2V0CmZhX21hY3U8LWZhKGRhdGFfbWFjdV9mYWNsZWFuZWQsbmZhY3RfbWFjdSxyb3Q9InByb21heCIpCgojIENoZWNrIHdodGhlciBhbnkgdmFyaWFibGUgZG8gbm90IGVudGVyIGluIGFueSBmYWN0b3IKbG9kIDwtIGZhX21hY3UkbG9hZGluZ3MKY2xhc3MobG9kKSA8LSAibWF0cml4Igpsb2QgPC0gZGF0YS5mcmFtZShsb2QpCmxvZCRhbnkgPC0gYXBwbHkobG9kLDEsZnVuY3Rpb24odilhbnkoYWJzKHYpPj0wLjIpICkKYXBwbHkobG9kWywxOm5mYWN0X21hY3VdLDEsbWF4ICkKbG9kW2xvZCRhbnk9PUYsXQojIE5PTkUsIGdvb2QhCgojIFBsb3QgdGhlIHJlc3VsdHMKYTwtZmFfbWFjdSRsb2FkaW5ncwpjbGFzcyhhKTwtIm1hdHJpeCIKY29sbmFtZXMoYSk8LXBhc3RlKCJGIiwxOm5mYWN0X21hY3Usc2VwPSIiKQphPC1hcy5kYXRhLmZyYW1lKGEpCmE8LXJvdW5kKGEsMikKYSREPC1yb3duYW1lcyhhKQphMSA8LSBhCmExJEQKYTEgPC0gbWVsdChhMSxpZC52YXJzPWMoIkQiKSkKYTEkeCA8LSBydW5pZihucm93KGExKSkKYTEkaW52IDwtIGlmZWxzZShhMSR2YWx1ZTwwLCJuZWciLCJwb3MiKQphMSR2YWx1ZVthYnMoYTEkdmFsdWUpPDAuMl0gPC0gMAphMSA8LSBhMVthMSR2YWx1ZSE9MCxdCgpnZ3Bsb3QoYTEpK2dlb21fYmFyKGFlcyh4PXJlb3JkZXIoRCwgdmFsdWUpICx5PXZhbHVlKSxzdGF0PSJpZGVudGl0eSIpK2ZhY2V0X3dyYXAofnZhcmlhYmxlLG5jb2wgPSAyLHNjYWxlcyA9ICJmcmVlX3kiKStjb29yZF9mbGlwKCkKI2RldGFjaCgicGFja2FnZTpnZ3Bsb3QyIiwgdW5sb2FkPVRSVUUpCgoKdmFyPC1sYXBwbHkoMTpuZmFjdF9tYWN1LGZ1bmN0aW9uKGYpdW5pcXVlKGEkRFthYnMoYVssZl0pPjAuMl0pKQpuYW1lcyh2YXIpPC1wYXN0ZShjb2xuYW1lcyhhWywxOm5mYWN0X21hY3VdKSkKCmFsIDwtIHNhcHBseSgxOmxlbmd0aCh2YXIpLGZ1bmN0aW9uKHYpIHBzeWNoOjphbHBoYShkYXRhX21hY3VfZmFjbGVhbmVkWyx2YXJbW3ZdXV0pJHRvdGFsJHN0ZC5hbHBoYSkKYWwKIyBBbHBoYSB0b3RhbApwc3ljaDo6YWxwaGEoZGF0YV9tYWN1X2ZhY2xlYW5lZCkkdG90YWwkc3RkLmFscGhhCiMgdGhleSBhcmUgb2suLi4KcHN5Y2g6OmFscGhhKGRhdGFfbWFjdSkkdG90YWwkc3RkLmFscGhhCgoKIyBUYWJsZSBvZiB0aGUgZmFjdG9ycwphJEQgPC0gTlVMTAphW2FicyhhKTwwLjJdIDwtIDAKZm9yKGkgaW4gMTpuY29sKGEpKXthWyxpXSA8LSBhcy5jaGFyYWN0ZXIoYVssaV0pfQoKYVthPT0iMCJdIDwtICIiCmxvYWRpbmdfZmFjdF9tYWN1IDwtIGEKbG9hZGluZ19mYWN0X21hY3UKCiMgRGlzY3JpbWluYW50IGFuYWx5c2lzOiBJbiB0aGlzIHNlY3Rpb24gaSB3aWxsIHRlc3QgaWYgdGhlIHZhbHVlcyBvZiB0aGUgdmFyaWFibGVzIGtlcHQgaW4gdGhlIGRhdGFmcmFtZSBieSB0aGUgZmFjdG9yaWFsIGFuYWx5c2lzIGFyZSBhYmxlIHRvIGRpc2NyaW1pbmF0ZSBiZXR3ZWVuIHN1YmplY3RzIGZvciB3aGljaCB0aGVpciB0b3RhbCBzY29yZSBsaWUgaW4gdGhlIDFzdCBhbmQgM3JkIHF1YXJ0aWxlLgpkaXNjcjwtdW5pcXVlKGRhdGFfbWFjdV9mYWNsZWFuZWQpCgojIERldHJtaW5lIHRoZSB0b3RhbCBzY29yZQoKZGlzY3IkcHVudGVnZ2lvPC1yb3dTdW1zKGRpc2NyKQoKIyBEaXZpZGNlIHRoZSBncm91cHMgb2YgcGVvcGxlIHdobyBsaWUgaW4gdGhlIDRydGggYW5kIDFzdCBxdWFyaWxlCgpoaXN0KGRpc2NyJHB1bnRlZ2dpbykKcXVhbnRpbGUoZGlzY3IkcHVudGVnZ2lvLG5hLnJtID0gVCkKCmRpc2NyMTwtdW5pcXVlKGRpc2NyW2Rpc2NyWyxuY29sKGRpc2NyKV08PXF1YW50aWxlKGRpc2NyWyxuY29sKGRpc2NyKV0sbmEucm0gPSBUKVsyXSxdKQpkaXNjcjI8LXVuaXF1ZShkaXNjcltkaXNjclssbmNvbChkaXNjcildPj1xdWFudGlsZShkaXNjclssbmNvbChkaXNjcildLG5hLnJtID0gVClbNF0sXSkKCiMgV2lsY294IFRlc3QgdGhlIHZhbHVlcyBvZiBlYWNoIHNpbmdsZSB2YXJpYWJsZSBjb21wYXJpbmcgdGhlIGdyb3VwIG9mIHBvcGxlIGxpZW4gaW4gdGhlIDFzdCBhbmQgM3JkIHF1YXJ0aWxlCgp0ZXN0PC1kYXRhLmZyYW1lKEl0ZW09Y29sbmFtZXMoZGlzY3JbLDE6KG5jb2woZGlzY3IpLTEpXSkscC52YWx1ZT1yZXAoMCwobmNvbChkaXNjciktMSkpKQoKZm9yKGkgaW4gMToobmNvbChkaXNjciktMSkpewogIHRlc3RbaSwyXTwtd2lsY294LnRlc3QoZGlzY3IxWyxpXSxkaXNjcjJbLGldLGFsdGVybmF0aXZlPSJ0d28uc2lkZWQiKSRwLnZhbHVlCn0KCnRlc3QgPC0gdGVzdFtvcmRlcih0ZXN0JHAudmFsdWUpLF0KdGVzdAojIHRoZXkgYWxsIGRpc2NyaW1pbmF0ZSEKCgojIENhbGN1bGF0ZSBmYWN0b3JzIG9uIHRoZSBkaXNjYXJkZWQgdmFyaWFibGVzCgpkYXRfZGlzYyA8LSBkYXRbLG5vX2NvcnJfbWFjdV0KCgojIGNoZWNrIGhvdyBtYW55IGZhY3RvcnMgc2hvdWxkIGJlIHVzZWQKZmFwIDwtIGZhLnBhcmFsbGVsKGRhdF9kaXNjKQpmYXAKCgoKbGlicmFyeShnZ3Bsb3QyKQpmYV9kaXNjIDwtIGZhKGRhdF9kaXNjLG5mYWN0b3JzID0gMSxyb3RhdGUgPSAib2JsaW1pbiIpCgoKCiMgUGxvdCB0aGUgcmVzdWx0cwphPC1mYV9kaXNjJGxvYWRpbmdzCmNsYXNzKGEpPC0ibWF0cml4Igpjb2xuYW1lcyhhKTwtcGFzdGUoIkYiLDEsc2VwPSIiKQphPC1hcy5kYXRhLmZyYW1lKGEpCmE8LXJvdW5kKGEsMikKYSREPC1yb3duYW1lcyhhKQphMSA8LSBhCmExJEQKYTEgPC0gbWVsdChhMSxpZC52YXJzPWMoIkQiKSkKYTEkeCA8LSBydW5pZihucm93KGExKSkKYTEkaW52IDwtIGlmZWxzZShhMSR2YWx1ZTwwLCJuZWciLCJwb3MiKQphMSR2YWx1ZVthYnMoYTEkdmFsdWUpPDAuMl0gPC0gMAphMSA8LSBhMVthMSR2YWx1ZSE9MCxdCgoKCmxpYnJhcnkoZ2dwbG90MikKZ2dwbG90KGExKStnZW9tX2JhcihhZXMoeD1yZW9yZGVyKEQsIHZhbHVlKSAseT12YWx1ZSksc3RhdD0iaWRlbnRpdHkiKStmYWNldF93cmFwKH52YXJpYWJsZSxuY29sID0gMixzY2FsZXMgPSAiZnJlZV95IikrY29vcmRfZmxpcCgpCiNkZXRhY2goInBhY2thZ2U6Z2dwbG90MiIsIHVubG9hZD1UUlVFKQoKCiMgUHJlZGljdCB0aGUgZmFjdG9ycwoKcHJlZCA8LSBhcy5kYXRhLmZyYW1lKHByZWRpY3QoZmFfbWFjdSxkYXRbLG5hbWVzKGRhdGFfbWFjdV9mYWNsZWFuZWQpXSkpCm5hbWVzKHByZWQpIDwtIHBhc3RlKCJGYWN0b3IiLDE6bmZhY3RfbWFjdSxzZXAgPSAiIikKCiMgUHJlZGljdCB0aGUgZmFjdG9yIGZyb20gdGhlIGRpc2NhcmRlZCB2YXJpYWJsZXMKcHJlZF9kaXNjIDwtIGFzLmRhdGEuZnJhbWUocHJlZGljdChmYV9kaXNjLGRhdFssbmFtZXMoZGF0X2Rpc2MpXSkpCm5hbWVzKHByZWRfZGlzYykgPC0gcGFzdGUoIkZhY3RvciIsNyxzZXAgPSAiIikKCgoKZmFjdG9ycyA8LSBjKG5hbWVzKHByZWQpLG5hbWVzKHByZWRfZGlzYykpCgoKZGF0X2NvbXBsZXRlIDwtIGNiaW5kKGRhdCxzY2FsZShwcmVkKSxzY2FsZShwcmVkX2Rpc2MpKQoKCmNvcnJwbG90KGNvcihkYXRfY29tcGxldGVbLGxpa2VydF92YXJpYWJsZXMyXSxkYXRfY29tcGxldGVbLGZhY3RvcnNdLHVzZSA9ICJwYWlyIikpCgoKYWxsX2NvbXBsZXRlIDwtICBjYmluZChhbGwscHJlZCxwcmVkX2Rpc2MpCgpkYXRfcGxvdCA8LSBtZWx0KGFsbF9jb21wbGV0ZSxpZC52YXJzID0gIkNvbnRleHQiLG1lYXN1cmUudmFycyA9IGZhY3RvcnMpCgpsaWJyYXJ5KGdncGxvdDIpCmdncGxvdChkYXRfcGxvdCkrZ2VvbV9ib3hwbG90KGFlcyh4PUNvbnRleHQseT12YWx1ZSxjb2xvcj1Db250ZXh0KSkrZmFjZXRfd3JhcCh+dmFyaWFibGUpK2Nvb3JkX2ZsaXAoKStndWlkZXMoY29sb3I9RikKCgoKbW9kIDwtIGxtKEZhY3RvcjF+Q29udGV4dCxkYXRhPWFsbF9jb21wbGV0ZSkKc3VtbWFyeShtb2QpCgptb2QgPC0gbG0oRmFjdG9yMn5Db250ZXh0LGRhdGE9YWxsX2NvbXBsZXRlKQpzdW1tYXJ5KG1vZCkKCnN1bW1hcnkobG0oRmFjdG9yNH5Db250ZXh0LGRhdGE9YWxsX2NvbXBsZXRlKSkKCnN1bW1hcnkobG0oRmFjdG9yNn5Db250ZXh0LGRhdGE9YWxsX2NvbXBsZXRlKSkKCnN1bW1hcnkobG0oRmFjdG9yN35Db250ZXh0LGRhdGE9YWxsX2NvbXBsZXRlKSkKCmBgYAoKIyMgQmFzaWMgZmFjdG9yIGFuYWx5c2lzOiA3IGZhY3RvcnMKClNldmVuLCBpcyB0aGUgbnVtYmVyIG9mIGZhY3RvcnMgdGhhdCB3b3VsZCBiZSBwcmVzZW50IGFjY29yZGluZyB0byB0aGUgc3R1ZHkgZGVzaWduLgpVc2luZyB2ZXJ5IHJlbGF4ZWQgY3V0b2ZmIG9mIDAuMiB0byBnZXQgcmlkIG9mIG5vdCBpbXBvcnRhbnQgdmFyaWFibGVzIGluIGVhY2ggZmFjdG9yLgoKYGBge3J9CnVzYWJsZV9pdGVtcyA8LSBsaWtlcnRfdmFyaWFibGVzMVshKGxpa2VydF92YXJpYWJsZXMxICVpbiUgYygibmVjZXNzaXR5MSIsImVkdWNhdGVkMSIsInJlY29ubmVjdC5jb21tMSIsICJzcGVha2Vyc21lbGIuY29tbTEiLCAiY29tZWNsb3Nlci5jb21tMSIpKV0KZGF0YTEgPC0gZGF0Wyx1c2FibGVfaXRlbXNdCgpmYWN0IDwtIDcKZmFfYmFzaWMgPC0gZmEoZGF0YTEsZmFjdCkKCmZhX2Jhc2ljCgojIHBsb3QgbG9hZGluZ3MKbG9hZGluZ3NfYmFzaWMgPC0gZmFfYmFzaWMkbG9hZGluZ3MKY2xhc3MobG9hZGluZ3NfYmFzaWMpPC0ibWF0cml4Igpjb2xuYW1lcyhsb2FkaW5nc19iYXNpYyk8LXBhc3RlKCJGIiwxOjcsc2VwPSIiKQpsb2FkaW5nc19iYXNpYzwtYXMuZGF0YS5mcmFtZShsb2FkaW5nc19iYXNpYykKbG9hZGluZ3NfYmFzaWM8LXJvdW5kKGxvYWRpbmdzX2Jhc2ljLDIpCmxvYWRpbmdzX2Jhc2ljJEQ8LXJvd25hbWVzKGxvYWRpbmdzX2Jhc2ljKQphMSA8LSBsb2FkaW5nc19iYXNpYwoKYTEgPC0gbWVsdChhMSxpZC52YXJzPWMoIkQiKSkKYTEkeCA8LSBydW5pZihucm93KGExKSkKYTEkaW52IDwtIGlmZWxzZShhMSR2YWx1ZTwwLCJuZWciLCJwb3MiKQphMSR2YWx1ZVthYnMoYTEkdmFsdWUpPDAuMl0gPC0gMAphMSA8LSBhMVthMSR2YWx1ZSE9MCxdCmExIDwtIGExICU+JSBzZXBhcmF0ZShELGludG8gPSBjKCJWYXJpYWJsZSIsIkl0ZW0iKSxyZW1vdmU9RkFMU0Usc2VwPSJbLl0iKQoKZ2dwbG90KGExKStnZW9tX2JhcihhZXMoeD1yZW9yZGVyKEQsIHZhbHVlKSAseT12YWx1ZSxmaWxsPUl0ZW0pLHN0YXQ9ImlkZW50aXR5IikrZmFjZXRfd3JhcCh+dmFyaWFibGUsbmNvbCA9IDIsc2NhbGVzID0gImZyZWVfeSIpK2Nvb3JkX2ZsaXAoKSArIGdlb21faGxpbmUoeWludGVyY2VwdCA9IGMoLTAuMywwLjMpLGxpbmV0eXBlPSJkb3R0ZWQiLGNvbG91cj0iZGFyayByZWQiKQoKIyBUYWJsZSBvZiB0aGUgZmFjdG9ycwpsb2FkaW5nc19iYXNpYyREIDwtIE5VTEwKbG9hZGluZ3NfYmFzaWNbYWJzKGxvYWRpbmdzX2Jhc2ljKTwwLjJdIDwtIDAKZm9yKGkgaW4gMTpuY29sKGxvYWRpbmdzX2Jhc2ljKSl7bG9hZGluZ3NfYmFzaWNbLGldIDwtIGFzLmNoYXJhY3Rlcihsb2FkaW5nc19iYXNpY1ssaV0pfQoKbG9hZGluZ3NfYmFzaWNbbG9hZGluZ3NfYmFzaWM9PSIwIl0gPC0gIiIKbG9hZGluZ19mYWN0X3JlZHVjZWQgPC0gbG9hZGluZ3NfYmFzaWMKbG9hZGluZ19mYWN0X3JlZHVjZWQKCiMgcHJlZGljdCB2YWx1ZXMgcGVyIHNhbXBsZXMKcHJlZF9iYXNpYyA8LSBhcy5kYXRhLmZyYW1lKHByZWRpY3QoZmFfYmFzaWMsZGF0YTEpKQpuYW1lcyhwcmVkX2Jhc2ljKSA8LSBwYXN0ZSgiRmFjdG9yIiwxOmZhY3Qsc2VwID0gIiIpCgpmYWN0b3JzIDwtIG5hbWVzKHByZWRfYmFzaWMpCmRhdF9jb21wbGV0ZV9iYXNpYyA8LSBjYmluZChkYXQsc2NhbGUocHJlZF9iYXNpYykpCmNvcnJwbG90KGNvcihkYXRfY29tcGxldGVfYmFzaWNbLHVzYWJsZV9pdGVtc10sZGF0X2NvbXBsZXRlX2Jhc2ljWyxmYWN0b3JzXSx1c2UgPSAicGFpciIpKQoKYWxsX2NvbXBsZXRlX2Jhc2ljIDwtICBjYmluZChhbGwscHJlZF9iYXNpYykKZGF0X3Bsb3RfYmFzaWMgPC0gbWVsdChhbGxfY29tcGxldGVfYmFzaWMsaWQudmFycyA9ICJDb250ZXh0IixtZWFzdXJlLnZhcnMgPSBmYWN0b3JzKQoKbGlicmFyeShnZ3Bsb3QyKQpnZ3Bsb3QoZGF0X3Bsb3RfYmFzaWMpK2dlb21fYm94cGxvdChhZXMoeD1Db250ZXh0LHk9dmFsdWUsY29sb3I9Q29udGV4dCkpK2ZhY2V0X3dyYXAofnZhcmlhYmxlKStjb29yZF9mbGlwKCkrZ3VpZGVzKGNvbG9yPUYpCgoKYGBgCgoKIyMgQmFzaWMgZmFjdG9yIGFuYWx5c2lzOiA2IGZhY3RvcnMKClVzaW5nIHZlcnkgcmVsYXhlZCBjdXRvZmYgb2YgMC4yIHRvIGdldCByaWQgb2Ygbm90IGltcG9ydGFudCB2YXJpYWJsZXMgaW4gZWFjaCBmYWN0b3IuCgpgYGB7cn0KdXNhYmxlX2l0ZW1zIDwtIGxpa2VydF92YXJpYWJsZXMxWyEobGlrZXJ0X3ZhcmlhYmxlczEgJWluJSBjKCJuZWNlc3NpdHkxIiwiZWR1Y2F0ZWQxIiwicmVjb25uZWN0LmNvbW0xIiwgInNwZWFrZXJzbWVsYi5jb21tMSIsICJjb21lY2xvc2VyLmNvbW0xIikpXQpkYXRhMSA8LSBkYXRbLHVzYWJsZV9pdGVtc10KCiMgRnJvbSBhIHN0YXRpc3RpY2FrIHBvaW50IG9mIHZpZXcgCmZhcCA8LSBmYS5wYXJhbGxlbChkYXRhMSkKZmFjdCA8LSA2CmZhX2Jhc2ljIDwtIGZhKGRhdGExLGZhY3QpCgpmYV9iYXNpYwoKIyBwbG90IGxvYWRpbmdzCmxvYWRpbmdzX2Jhc2ljIDwtIGZhX2Jhc2ljJGxvYWRpbmdzCmNsYXNzKGxvYWRpbmdzX2Jhc2ljKTwtIm1hdHJpeCIKY29sbmFtZXMobG9hZGluZ3NfYmFzaWMpPC1wYXN0ZSgiRiIsMTo2LHNlcD0iIikKbG9hZGluZ3NfYmFzaWM8LWFzLmRhdGEuZnJhbWUobG9hZGluZ3NfYmFzaWMpCmxvYWRpbmdzX2Jhc2ljPC1yb3VuZChsb2FkaW5nc19iYXNpYywyKQpsb2FkaW5nc19iYXNpYyREPC1yb3duYW1lcyhsb2FkaW5nc19iYXNpYykKYTEgPC0gbG9hZGluZ3NfYmFzaWMKCmExIDwtIG1lbHQoYTEsaWQudmFycz1jKCJEIikpCmExJHggPC0gcnVuaWYobnJvdyhhMSkpCmExJGludiA8LSBpZmVsc2UoYTEkdmFsdWU8MCwibmVnIiwicG9zIikKYTEkdmFsdWVbYWJzKGExJHZhbHVlKTwwLjJdIDwtIDAKYTEgPC0gYTFbYTEkdmFsdWUhPTAsXQphMSA8LSBhMSAlPiUgc2VwYXJhdGUoRCxpbnRvID0gYygiVmFyaWFibGUiLCJJdGVtIikscmVtb3ZlPUZBTFNFLHNlcD0iWy5dIikKCmdncGxvdChhMSkrZ2VvbV9iYXIoYWVzKHg9cmVvcmRlcihELCB2YWx1ZSkgLHk9dmFsdWUsZmlsbD1JdGVtKSxzdGF0PSJpZGVudGl0eSIpK2ZhY2V0X3dyYXAofnZhcmlhYmxlLG5jb2wgPSAyLHNjYWxlcyA9ICJmcmVlX3kiKStjb29yZF9mbGlwKCkrIGdlb21faGxpbmUoeWludGVyY2VwdCA9IGMoLTAuMywwLjMpLGxpbmV0eXBlPSJkb3R0ZWQiLGNvbG91cj0iZGFyayByZWQiKQoKIyBUYWJsZSBvZiB0aGUgZmFjdG9ycwpsb2FkaW5nc19iYXNpYyREIDwtIE5VTEwKbG9hZGluZ3NfYmFzaWNbYWJzKGxvYWRpbmdzX2Jhc2ljKTwwLjJdIDwtIDAKZm9yKGkgaW4gMTpuY29sKGxvYWRpbmdzX2Jhc2ljKSl7bG9hZGluZ3NfYmFzaWNbLGldIDwtIGFzLmNoYXJhY3Rlcihsb2FkaW5nc19iYXNpY1ssaV0pfQoKbG9hZGluZ3NfYmFzaWNbbG9hZGluZ3NfYmFzaWMgPT0gIjAiXSA8LSAiIgpsb2FkaW5nX2ZhY3RfcmVkdWNlZCA8LSBsb2FkaW5nc19iYXNpYwpsb2FkaW5nX2ZhY3RfcmVkdWNlZAoKIyBwcmVkaWN0IHZhbHVlcyBwZXIgc2FtcGxlcwpwcmVkX2Jhc2ljIDwtIGFzLmRhdGEuZnJhbWUocHJlZGljdChmYV9iYXNpYyxkYXRhMSkpCm5hbWVzKHByZWRfYmFzaWMpIDwtIHBhc3RlKCJGYWN0b3IiLDE6ZmFjdCxzZXAgPSAiIikKCmZhY3RvcnMgPC0gbmFtZXMocHJlZF9iYXNpYykKZGF0X2NvbXBsZXRlX2Jhc2ljIDwtIGNiaW5kKGRhdCxzY2FsZShwcmVkX2Jhc2ljKSkKY29ycnBsb3QoY29yKGRhdF9jb21wbGV0ZV9iYXNpY1ssdXNhYmxlX2l0ZW1zXSxkYXRfY29tcGxldGVfYmFzaWNbLGZhY3RvcnNdLHVzZSA9ICJwYWlyIikpCgphbGxfY29tcGxldGVfYmFzaWMgPC0gIGNiaW5kKGFsbCxwcmVkX2Jhc2ljKQpkYXRfcGxvdF9iYXNpYyA8LSBtZWx0KGFsbF9jb21wbGV0ZV9iYXNpYyxpZC52YXJzID0gIkNvbnRleHQiLG1lYXN1cmUudmFycyA9IGZhY3RvcnMpCgpsaWJyYXJ5KGdncGxvdDIpCmdncGxvdChkYXRfcGxvdF9iYXNpYykgKyBnZW9tX2JveHBsb3QoYWVzKHg9Q29udGV4dCx5PXZhbHVlLGNvbG9yPUNvbnRleHQpKStmYWNldF93cmFwKH52YXJpYWJsZSkrY29vcmRfZmxpcCgpK2d1aWRlcyhjb2xvcj1GKQoKIyBlcnJvciBiYXIgCnN1bV9zdGF0IDwtIGRhdF9wbG90X2Jhc2ljICU+JSBncm91cF9ieShDb250ZXh0LHZhcmlhYmxlKSAlPiUKICBzdW1tYXJpc2UobWVhbkZhYyA9IG1lYW4odmFsdWUsbmEucm09VFJVRSksCiAgICAgICAgICAgIHN0ZEZhYyA9IHNkKHZhbHVlLG5hLnJtPVRSVUUpLAogICAgICAgICAgICBuT2JzID0gbGVuZ3RoKENvbnRleHRbIWlzLm5hKHZhbHVlKV0pKSAlPiUKICBtdXRhdGUoc3RkTWVhbiA9IHN0ZEZhYy9zcXJ0KG5PYnMpKQoKZ2dwbG90KHN1bV9zdGF0LGFlcyh4PUNvbnRleHQseT1tZWFuRmFjLGNvbG91cj1Db250ZXh0KSkgKyAKZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj1tZWFuRmFjLXN0ZE1lYW4sIHltYXg9bWVhbkZhYytzdGRNZWFuKSkgKyBmYWNldF93cmFwKH52YXJpYWJsZSxzY2FsZXM9ImZyZWVfeSIpICsgZ2VvbV9wb2ludCgpICt0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQoKZ2dwbG90KHN1bV9zdGF0LGFlcyh4PXZhcmlhYmxlLHk9bWVhbkZhYyxjb2xvdXI9dmFyaWFibGUpKSArIApnZW9tX2Vycm9yYmFyKGFlcyh5bWluPW1lYW5GYWMtc3RkTWVhbiwgeW1heD1tZWFuRmFjK3N0ZE1lYW4pKSArIGZhY2V0X3dyYXAofkNvbnRleHQsc2NhbGVzPSJmcmVlX3kiKSArIGdlb21fcG9pbnQoKQoKa2FibGUoc3VtX3N0YXQpCmBgYAoKIyBGYWN0b3IgYW5hbHlzaXMgb24gZWFjaCBjb250ZXh0IHNlcGFyYXRlbHkKCmBgYHtyfQp1c2FibGVfaXRlbXMgPC0gbGlrZXJ0X3ZhcmlhYmxlczFbIShsaWtlcnRfdmFyaWFibGVzMSAlaW4lIGMoIm5lY2Vzc2l0eTEiLCJlZHVjYXRlZDEiLCJyZWNvbm5lY3QuY29tbTEiLCAic3BlYWtlcnNtZWxiLmNvbW0xIiwgImNvbWVjbG9zZXIuY29tbTEiKSldCiMgCmRhdCA8LSBhbGxbLGModXNhYmxlX2l0ZW1zLCJDb250ZXh0IildCmRhdF9ub05BIDwtIGRhdFtyb3dTdW1zKGlzLm5hKGRhdCkpID09IDAsXQphbGxfbm9OQSA8LSBhbGxbcm93U3Vtcyhpcy5uYShkYXQpKSA9PSAwLF0KCiMgbmEgdG8gcmVtb3ZlCnRhYmxlKHJvd1N1bXMoaXMubmEoZGF0KSksZGF0JENvbnRleHQpCgpnZXRfcmVzaWR1YWxzIDwtIGZ1bmN0aW9uKGl0ZW0scHJlZCA9IGRhdCRDb250ZXh0KXsKICBtb2QgPC0gbG0oaXRlbSB+IHByZWQpCiAgcmV0dXJuKG1vZCRyZXNpZHVhbHMpCn0KCmRhdF9vbmx5SXRlbXMgPC0gZGF0X25vTkFbLHVzYWJsZV9pdGVtc10KI2RhdGExIDwtIGRhdFssdXNhYmxlX2l0ZW1zXQojIGRhdF9vbmx5SXRlbXMgPC0gZGF0YTEKCmFwcGx5Z2V0UmVzIDwtIGFwcGx5KGFzLm1hdHJpeChkYXRfb25seUl0ZW1zKSwyLGdldF9yZXNpZHVhbHMscHJlZD1kYXRfbm9OQSRDb250ZXh0KQoKIyBGYWN0YW5hbCAKIyBGcm9tIGEgc3RhdGlzdGljYWsgcG9pbnQgb2YgdmlldyAKZmFwIDwtIGZhLnBhcmFsbGVsKGFwcGx5Z2V0UmVzKQpmYWN0IDwtIDYKZmFfYmFzaWMgPC0gZmEoYXBwbHlnZXRSZXMsZmFjdCkKCmZhX2Jhc2ljCgojIHBsb3QgbG9hZGluZ3MKbG9hZGluZ3NfYmFzaWMgPC0gZmFfYmFzaWMkbG9hZGluZ3MKY2xhc3MobG9hZGluZ3NfYmFzaWMpPC0ibWF0cml4Igpjb2xuYW1lcyhsb2FkaW5nc19iYXNpYyk8LXBhc3RlKCJGIiwxOjYsc2VwPSIiKQpsb2FkaW5nc19iYXNpYzwtYXMuZGF0YS5mcmFtZShsb2FkaW5nc19iYXNpYykKbG9hZGluZ3NfYmFzaWM8LXJvdW5kKGxvYWRpbmdzX2Jhc2ljLDIpCmxvYWRpbmdzX2Jhc2ljJEQ8LXJvd25hbWVzKGxvYWRpbmdzX2Jhc2ljKQphMSA8LSBsb2FkaW5nc19iYXNpYwoKYTEgPC0gbWVsdChhMSxpZC52YXJzPWMoIkQiKSkKYTEkeCA8LSBydW5pZihucm93KGExKSkKYTEkaW52IDwtIGlmZWxzZShhMSR2YWx1ZTwwLCJuZWciLCJwb3MiKQphMSR2YWx1ZVthYnMoYTEkdmFsdWUpPDAuMl0gPC0gMAphMSA8LSBhMVthMSR2YWx1ZSE9MCxdCmExIDwtIGExICU+JSBzZXBhcmF0ZShELGludG8gPSBjKCJWYXJpYWJsZSIsIkl0ZW0iKSxyZW1vdmU9RkFMU0Usc2VwPSJbLl0iKQoKZ2dwbG90KGExKStnZW9tX2JhcihhZXMoeD1yZW9yZGVyKEQsIHZhbHVlKSAseT12YWx1ZSxmaWxsPUl0ZW0pLHN0YXQ9ImlkZW50aXR5IikrZmFjZXRfd3JhcCh+dmFyaWFibGUsbmNvbCA9IDIsc2NhbGVzID0gImZyZWVfeSIpK2Nvb3JkX2ZsaXAoKSsgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gYygtMC4zLDAuMyksbGluZXR5cGU9ImRvdHRlZCIsY29sb3VyPSJkYXJrIHJlZCIpCgojIFRhYmxlIG9mIHRoZSBmYWN0b3JzCmxvYWRpbmdzX2Jhc2ljJEQgPC0gTlVMTApsb2FkaW5nc19iYXNpY1thYnMobG9hZGluZ3NfYmFzaWMpPDAuMl0gPC0gMApmb3IoaSBpbiAxOm5jb2wobG9hZGluZ3NfYmFzaWMpKXtsb2FkaW5nc19iYXNpY1ssaV0gPC0gYXMuY2hhcmFjdGVyKGxvYWRpbmdzX2Jhc2ljWyxpXSl9Cgpsb2FkaW5nc19iYXNpY1tsb2FkaW5nc19iYXNpYyA9PSAiMCJdIDwtICIiCmxvYWRpbmdfZmFjdF9yZWR1Y2VkIDwtIGxvYWRpbmdzX2Jhc2ljCmxvYWRpbmdfZmFjdF9yZWR1Y2VkCgojIHByZWRpY3QgdmFsdWVzIHBlciBzYW1wbGVzCnByZWRfYmFzaWMgPC0gYXMuZGF0YS5mcmFtZShwcmVkaWN0KGZhX2Jhc2ljLGRhdF9vbmx5SXRlbXMpKQpuYW1lcyhwcmVkX2Jhc2ljKSA8LSBwYXN0ZSgiRmFjdG9yIiwxOmZhY3Qsc2VwID0gIiIpCgpmYWN0b3JzIDwtIG5hbWVzKHByZWRfYmFzaWMpCmRhdF9jb21wbGV0ZV9iYXNpYyA8LSBjYmluZChkYXRfb25seUl0ZW1zLHNjYWxlKHByZWRfYmFzaWMpKQpjb3JycGxvdChjb3IoZGF0X2NvbXBsZXRlX2Jhc2ljWyx1c2FibGVfaXRlbXNdLGRhdF9jb21wbGV0ZV9iYXNpY1ssZmFjdG9yc10sdXNlID0gInBhaXIiKSkKCmFsbF9jb21wbGV0ZV9iYXNpYyA8LSAgY2JpbmQoYWxsX25vTkEscHJlZF9iYXNpYykKZGF0X3Bsb3RfYmFzaWMgPC0gbWVsdChhbGxfY29tcGxldGVfYmFzaWMsaWQudmFycyA9ICJDb250ZXh0IixtZWFzdXJlLnZhcnMgPSBmYWN0b3JzKQoKbGlicmFyeShnZ3Bsb3QyKQpnZ3Bsb3QoZGF0X3Bsb3RfYmFzaWMpICsgZ2VvbV9ib3hwbG90KGFlcyh4PUNvbnRleHQseT12YWx1ZSxjb2xvcj1Db250ZXh0KSkrZmFjZXRfd3JhcCh+dmFyaWFibGUpK2Nvb3JkX2ZsaXAoKStndWlkZXMoY29sb3I9RikKCiMgZXJyb3IgYmFyIApzdW1fc3RhdCA8LSBkYXRfcGxvdF9iYXNpYyAlPiUgZ3JvdXBfYnkoQ29udGV4dCx2YXJpYWJsZSkgJT4lCiAgc3VtbWFyaXNlKG1lYW5GYWMgPSBtZWFuKHZhbHVlLG5hLnJtPVRSVUUpLAogICAgICAgICAgICBzdGRGYWMgPSBzZCh2YWx1ZSxuYS5ybT1UUlVFKSwKICAgICAgICAgICAgbk9icyA9IGxlbmd0aChDb250ZXh0WyFpcy5uYSh2YWx1ZSldKSkgJT4lCiAgbXV0YXRlKHN0ZE1lYW4gPSBzdGRGYWMvc3FydChuT2JzKSkKCmdncGxvdChzdW1fc3RhdCxhZXMoeD1Db250ZXh0LHk9bWVhbkZhYyxjb2xvdXI9Q29udGV4dCkpICsgCmdlb21fZXJyb3JiYXIoYWVzKHltaW49bWVhbkZhYy1zdGRNZWFuLCB5bWF4PW1lYW5GYWMrc3RkTWVhbikpICsgZmFjZXRfd3JhcCh+dmFyaWFibGUsc2NhbGVzPSJmcmVlX3kiKSArIGdlb21fcG9pbnQoKSArdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkKCmdncGxvdChzdW1fc3RhdCxhZXMoeD12YXJpYWJsZSx5PW1lYW5GYWMsY29sb3VyPXZhcmlhYmxlKSkgKyAKZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj1tZWFuRmFjLXN0ZE1lYW4sIHltYXg9bWVhbkZhYytzdGRNZWFuKSkgKyBmYWNldF93cmFwKH5Db250ZXh0LHNjYWxlcz0iZnJlZV95IikgKyBnZW9tX3BvaW50KCkKCmthYmxlKHN1bV9zdGF0KQoKIyMgbGluZWFyIG1vZGVsCgpgYGAKCiMjIERlbW9ncmFwaGljcwoKYGBge3J9CmRlbW9ncmFwaGljc192YXIgPC0gYygiQWdlIiwiR2VuZGVyIiwiTDEiLCJzcGVhay5vdGhlci5MMiIsInN0dWR5Lm90aGVyLkwyIiwib3JpZ2lucyIsInllYXIuc3R1ZHlMMiIsIm90aGVyNS5vdGhlci53YXlzIiwiZGVncmVlIiwicm9sZUwyLmRlZ3JlZSIsInN0dWR5LnllYXIiLCJwcm9mIiwiTDIuVkNFIiwidW5pMS55ZWFyIiwiQ29udGV4dCIpCgoKCmBgYAoKCgojIyBPbmx5IEVuZ2xpc2ggaW4gR2VybWFueQoKYGBge3IgZXZhbD1GQUxTRX0KdXNhYmxlX2l0ZW1zIDwtIGxpa2VydF92YXJpYWJsZXMxWyEobGlrZXJ0X3ZhcmlhYmxlczEgJWluJSBjKCJuZWNlc3NpdHkxIiwiZWR1Y2F0ZWQxIiwicmVjb25uZWN0LmNvbW0xIiwgInNwZWFrZXJzbWVsYi5jb21tMSIsICJjb21lY2xvc2VyLmNvbW0xIikpXQpkYXRhMSA8LSBhbGxbYWxsJENvbnRleHQgJWluJSAiRW5nbGlzaCBpbiBHZXJtYW55Iix1c2FibGVfaXRlbXNdCgojIEZyb20gYSBzdGF0aXN0aWNhayBwb2ludCBvZiB2aWV3IApmYXAgPC0gZmEucGFyYWxsZWwoZGF0YTEpCmZhY3QgPC0gNgpmYV9iYXNpYyA8LSBmYShkYXRhMSxmYWN0KQoKZmFfYmFzaWMKCiMgcGxvdCBsb2FkaW5ncwpsb2FkaW5nc19iYXNpYyA8LSBmYV9iYXNpYyRsb2FkaW5ncwpjbGFzcyhsb2FkaW5nc19iYXNpYyk8LSJtYXRyaXgiCmNvbG5hbWVzKGxvYWRpbmdzX2Jhc2ljKTwtcGFzdGUoIkYiLDE6NixzZXA9IiIpCmxvYWRpbmdzX2Jhc2ljPC1hcy5kYXRhLmZyYW1lKGxvYWRpbmdzX2Jhc2ljKQpsb2FkaW5nc19iYXNpYzwtcm91bmQobG9hZGluZ3NfYmFzaWMsMikKbG9hZGluZ3NfYmFzaWMkRDwtcm93bmFtZXMobG9hZGluZ3NfYmFzaWMpCmExIDwtIGxvYWRpbmdzX2Jhc2ljCgphMSA8LSBtZWx0KGExLGlkLnZhcnM9YygiRCIpKQphMSR4IDwtIHJ1bmlmKG5yb3coYTEpKQphMSRpbnYgPC0gaWZlbHNlKGExJHZhbHVlPDAsIm5lZyIsInBvcyIpCmExJHZhbHVlW2FicyhhMSR2YWx1ZSk8MC4yXSA8LSAwCmExIDwtIGExW2ExJHZhbHVlIT0wLF0KYTEgPC0gYTEgJT4lIHNlcGFyYXRlKEQsaW50byA9IGMoIlZhcmlhYmxlIiwiSXRlbSIpLHJlbW92ZT1GQUxTRSxzZXA9IlsuXSIpCgpnZ3Bsb3QoYTEpK2dlb21fYmFyKGFlcyh4PXJlb3JkZXIoRCwgdmFsdWUpICx5PXZhbHVlLGZpbGw9SXRlbSksc3RhdD0iaWRlbnRpdHkiKStmYWNldF93cmFwKH52YXJpYWJsZSxuY29sID0gMixzY2FsZXMgPSAiZnJlZV95IikrY29vcmRfZmxpcCgpKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBjKC0wLjMsMC4zKSxsaW5ldHlwZT0iZG90dGVkIixjb2xvdXI9ImRhcmsgcmVkIikKCiMgVGFibGUgb2YgdGhlIGZhY3RvcnMKbG9hZGluZ3NfYmFzaWMkRCA8LSBOVUxMCmxvYWRpbmdzX2Jhc2ljW2Ficyhsb2FkaW5nc19iYXNpYyk8MC4yXSA8LSAwCmZvcihpIGluIDE6bmNvbChsb2FkaW5nc19iYXNpYykpe2xvYWRpbmdzX2Jhc2ljWyxpXSA8LSBhcy5jaGFyYWN0ZXIobG9hZGluZ3NfYmFzaWNbLGldKX0KCmxvYWRpbmdzX2Jhc2ljW2xvYWRpbmdzX2Jhc2ljPT0iMCJdIDwtICIiCmxvYWRpbmdfZmFjdF9yZWR1Y2VkIDwtIGxvYWRpbmdzX2Jhc2ljCmxvYWRpbmdfZmFjdF9yZWR1Y2VkCgojIHByZWRpY3QgdmFsdWVzIHBlciBzYW1wbGVzCnByZWRfYmFzaWMgPC0gYXMuZGF0YS5mcmFtZShwcmVkaWN0KGZhX2Jhc2ljLGRhdGExKSkKbmFtZXMocHJlZF9iYXNpYykgPC0gcGFzdGUoIkZhY3RvciIsMTpmYWN0LHNlcCA9ICIiKQoKZmFjdG9ycyA8LSBuYW1lcyhwcmVkX2Jhc2ljKQpkYXRfY29tcGxldGVfYmFzaWMgPC0gY2JpbmQoZGF0LHNjYWxlKHByZWRfYmFzaWMpKQpjb3JycGxvdChjb3IoZGF0X2NvbXBsZXRlX2Jhc2ljWyx1c2FibGVfaXRlbXNdLGRhdF9jb21wbGV0ZV9iYXNpY1ssZmFjdG9yc10sdXNlID0gInBhaXIiKSkKCmFsbF9jb21wbGV0ZV9iYXNpYyA8LSAgY2JpbmQoYWxsLHByZWRfYmFzaWMpCmRhdF9wbG90X2Jhc2ljIDwtIG1lbHQoYWxsX2NvbXBsZXRlX2Jhc2ljLGlkLnZhcnMgPSAiQ29udGV4dCIsbWVhc3VyZS52YXJzID0gZmFjdG9ycykKCmxpYnJhcnkoZ2dwbG90MikKZ2dwbG90KGRhdF9wbG90X2Jhc2ljKStnZW9tX2JveHBsb3QoYWVzKHg9Q29udGV4dCx5PXZhbHVlLGNvbG9yPUNvbnRleHQpKStmYWNldF93cmFwKH52YXJpYWJsZSkrY29vcmRfZmxpcCgpK2d1aWRlcyhjb2xvcj1GKQoKCmBgYA==